Merge "Add tests for Paint#getTextRunAdvances"
diff --git a/Android.mk b/Android.mk
index d48887d..9029f4e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -303,6 +303,7 @@
core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \
core/java/com/android/internal/textservice/ITextServicesManager.aidl \
core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \
+ core/java/com/android/internal/view/IDropPermissionHolder.aidl \
core/java/com/android/internal/view/IInputContext.aidl \
core/java/com/android/internal/view/IInputContextCallback.aidl \
core/java/com/android/internal/view/IInputMethod.aidl \
@@ -482,6 +483,7 @@
frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl \
frameworks/base/wifi/java/android/net/wifi/WpsInfo.aidl \
frameworks/base/wifi/java/android/net/wifi/ScanResult.aidl \
+ frameworks/base/wifi/java/android/net/wifi/ScanInfo.aidl \
frameworks/base/wifi/java/android/net/wifi/WifiEnterpriseConfig.aidl \
frameworks/base/wifi/java/android/net/wifi/WifiConfiguration.aidl \
frameworks/base/wifi/java/android/net/wifi/WifiInfo.aidl \
diff --git a/api/current.txt b/api/current.txt
index d829272..7040e4d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -323,6 +323,7 @@
field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
field public static final int buttonBarStyle = 16843566; // 0x101032e
+ field public static final int buttonGravity = 16844029; // 0x10104fd
field public static final int buttonStyle = 16842824; // 0x1010048
field public static final int buttonStyleInset = 16842826; // 0x101004a
field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -372,6 +373,7 @@
field public static final int codes = 16843330; // 0x1010242
field public static final int collapseColumns = 16843083; // 0x101014b
field public static final int collapseContentDescription = 16843984; // 0x10104d0
+ field public static final int collapseIcon = 16844030; // 0x10104fe
field public static final int color = 16843173; // 0x10101a5
field public static final int colorAccent = 16843829; // 0x1010435
field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -412,6 +414,7 @@
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contextClickable = 16844007; // 0x10104e7
+ field public static final int contextPopupMenuStyle = 16844032; // 0x1010500
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -777,6 +780,7 @@
field public static final int layout_y = 16843136; // 0x1010180
field public static final int left = 16843181; // 0x10101ad
field public static final int letterSpacing = 16843958; // 0x10104b6
+ field public static final int level = 16844031; // 0x10104ff
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
field public static final int lines = 16843092; // 0x1010154
@@ -809,6 +813,7 @@
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int matchOrder = 16843855; // 0x101044f
field public static final int max = 16843062; // 0x1010136
+ field public static final int maxButtonHeight = 16844028; // 0x10104fc
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
field public static final int maxHeight = 16843040; // 0x1010120
@@ -1216,6 +1221,7 @@
field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
+ field public static final int textAppearancePopupMenuHeader = 16844033; // 0x1010501
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1284,6 +1290,11 @@
field public static final int tintMode = 16843771; // 0x10103fb
field public static final int title = 16843233; // 0x10101e1
field public static final int titleCondensed = 16843234; // 0x10101e2
+ field public static final int titleMargin = 16844023; // 0x10104f7
+ field public static final int titleMarginBottom = 16844027; // 0x10104fb
+ field public static final int titleMarginEnd = 16844025; // 0x10104f9
+ field public static final int titleMarginStart = 16844024; // 0x10104f8
+ field public static final int titleMarginTop = 16844026; // 0x10104fa
field public static final int titleTextAppearance = 16843822; // 0x101042e
field public static final int titleTextColor = 16844003; // 0x10104e3
field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -12303,7 +12314,6 @@
method public int getIntrinsicWidth();
method public int getLayoutDirection();
method public final int getLevel();
- method public final float getLevelFloat();
method public int getMinimumHeight();
method public int getMinimumWidth();
method public abstract int getOpacity();
@@ -12323,7 +12333,6 @@
method protected void onBoundsChange(android.graphics.Rect);
method public boolean onLayoutDirectionChanged(int);
method protected boolean onLevelChange(int);
- method protected boolean onLevelChange(float);
method protected boolean onStateChange(int[]);
method public static int resolveOpacity(int, int);
method public void scheduleSelf(java.lang.Runnable, long);
@@ -12341,15 +12350,12 @@
method public void setHotspotBounds(int, int, int, int);
method public final boolean setLayoutDirection(int);
method public final boolean setLevel(int);
- method public final boolean setLevel(float);
method public boolean setState(int[]);
method public void setTint(int);
method public void setTintList(android.content.res.ColorStateList);
method public void setTintMode(android.graphics.PorterDuff.Mode);
method public boolean setVisible(boolean, boolean);
method public void unscheduleSelf(java.lang.Runnable);
- field public static final int MAX_LEVEL = 10000; // 0x2710
- field public static final float MAX_LEVEL_FLOAT = 10000.0f;
}
public static abstract interface Drawable.Callback {
@@ -18127,6 +18133,7 @@
method public boolean importFile(int, java.lang.String);
method public boolean importFile(int, android.os.ParcelFileDescriptor);
method public boolean open(android.hardware.usb.UsbDeviceConnection);
+ method public android.mtp.MtpEvent readEvent(android.os.CancellationSignal);
method public boolean sendObject(int, int, android.os.ParcelFileDescriptor);
method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo);
}
@@ -18138,6 +18145,11 @@
method public final java.lang.String getVersion();
}
+ public class MtpEvent {
+ ctor public MtpEvent();
+ method public int getEventCode();
+ }
+
public final class MtpObjectInfo {
method public final int getAssociationDesc();
method public final int getAssociationType();
@@ -18247,6 +18259,7 @@
field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+ field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -19153,6 +19166,22 @@
package android.net.wifi {
+ public class ScanInfo implements android.os.Parcelable {
+ ctor public ScanInfo(android.net.wifi.ScanResult);
+ ctor public ScanInfo(long, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int);
+ method public int describeContents();
+ method public long getBssid();
+ method public byte[] getIconData();
+ method public java.lang.String getIconType();
+ method public java.lang.String getName();
+ method public int getOsuIdentity();
+ method public int getRssi();
+ method public android.net.wifi.ScanResult getScanResult();
+ method public java.lang.String getServiceDescription();
+ method public java.lang.String getSsid();
+ method public void writeToParcel(android.os.Parcel, int);
+ }
+
public class ScanResult implements android.os.Parcelable {
method public int describeContents();
method public boolean is80211mcResponder();
@@ -19353,6 +19382,7 @@
method public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
method public android.net.wifi.WifiInfo getConnectionInfo();
method public android.net.DhcpInfo getDhcpInfo();
+ method public java.util.List<android.net.wifi.ScanInfo> getScanInfos();
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
method public int getWifiState();
method public boolean is5GHzBandSupported();
@@ -19368,6 +19398,7 @@
method public boolean reconnect();
method public boolean removeNetwork(int);
method public boolean saveConfiguration();
+ method public void setOsuSelection(int);
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
method public boolean setWifiEnabled(boolean);
@@ -34807,6 +34838,7 @@
method public int getAction();
method public android.content.ClipData getClipData();
method public android.content.ClipDescription getClipDescription();
+ method public android.view.DropPermissionHolder getDropPermissionHolder();
method public java.lang.Object getLocalState();
method public boolean getResult();
method public float getX();
@@ -34821,6 +34853,14 @@
field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
}
+ public class DropPermissionHolder implements android.os.Parcelable {
+ method public int describeContents();
+ method public void grant();
+ method public void revoke();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+ }
+
public class FocusFinder {
method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
@@ -36509,6 +36549,7 @@
method public void setY(float);
method public void setZ(float);
method public boolean showContextMenu();
+ method public boolean showContextMenu(float, float);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
method public void startAnimation(android.view.animation.Animation);
@@ -36524,6 +36565,11 @@
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+ field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
+ field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
+ field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
+ field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
+ field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
@@ -36970,6 +37016,7 @@
method public void setTransitionGroup(boolean);
method public boolean shouldDelayChildPressedState();
method public boolean showContextMenuForChild(android.view.View);
+ method public boolean showContextMenuForChild(android.view.View, float, float);
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
method public void startLayoutAnimation();
@@ -37091,6 +37138,7 @@
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View);
+ method public abstract boolean showContextMenuForChild(android.view.View, float, float);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
}
@@ -40869,6 +40917,7 @@
method public int getInputMethodMode();
method public int getMaxAvailableHeight(android.view.View);
method public int getMaxAvailableHeight(android.view.View, int);
+ method public int getMaxAvailableHeight(android.view.View, int, boolean);
method public boolean getOverlapAnchor();
method public int getSoftInputMode();
method public int getWidth();
@@ -40955,6 +41004,7 @@
method public void setInterpolator(android.view.animation.Interpolator);
method public synchronized void setMax(int);
method public synchronized void setProgress(int);
+ method public void setProgress(int, boolean);
method public void setProgressBackgroundTintList(android.content.res.ColorStateList);
method public void setProgressBackgroundTintMode(android.graphics.PorterDuff.Mode);
method public void setProgressDrawable(android.graphics.drawable.Drawable);
@@ -41951,6 +42001,10 @@
method public int getPopupTheme();
method public java.lang.CharSequence getSubtitle();
method public java.lang.CharSequence getTitle();
+ method public int getTitleMarginBottom();
+ method public int getTitleMarginEnd();
+ method public int getTitleMarginStart();
+ method public int getTitleMarginTop();
method public boolean hasExpandedActionView();
method public boolean hideOverflowMenu();
method public void inflateMenu(int);
@@ -41976,6 +42030,11 @@
method public void setSubtitleTextColor(int);
method public void setTitle(int);
method public void setTitle(java.lang.CharSequence);
+ method public void setTitleMargin(int, int, int, int);
+ method public void setTitleMarginBottom(int);
+ method public void setTitleMarginEnd(int);
+ method public void setTitleMarginStart(int);
+ method public void setTitleMarginTop(int);
method public void setTitleTextAppearance(android.content.Context, int);
method public void setTitleTextColor(int);
method public boolean showOverflowMenu();
diff --git a/api/system-current.txt b/api/system-current.txt
index 5905056..9b85df4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -415,6 +415,7 @@
field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
field public static final int buttonBarStyle = 16843566; // 0x101032e
+ field public static final int buttonGravity = 16844029; // 0x10104fd
field public static final int buttonStyle = 16842824; // 0x1010048
field public static final int buttonStyleInset = 16842826; // 0x101004a
field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -464,6 +465,7 @@
field public static final int codes = 16843330; // 0x1010242
field public static final int collapseColumns = 16843083; // 0x101014b
field public static final int collapseContentDescription = 16843984; // 0x10104d0
+ field public static final int collapseIcon = 16844030; // 0x10104fe
field public static final int color = 16843173; // 0x10101a5
field public static final int colorAccent = 16843829; // 0x1010435
field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -504,6 +506,7 @@
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contextClickable = 16844007; // 0x10104e7
+ field public static final int contextPopupMenuStyle = 16844032; // 0x1010500
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -869,6 +872,7 @@
field public static final int layout_y = 16843136; // 0x1010180
field public static final int left = 16843181; // 0x10101ad
field public static final int letterSpacing = 16843958; // 0x10104b6
+ field public static final int level = 16844031; // 0x10104ff
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
field public static final int lines = 16843092; // 0x1010154
@@ -901,6 +905,7 @@
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int matchOrder = 16843855; // 0x101044f
field public static final int max = 16843062; // 0x1010136
+ field public static final int maxButtonHeight = 16844028; // 0x10104fc
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
field public static final int maxHeight = 16843040; // 0x1010120
@@ -1312,6 +1317,7 @@
field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
+ field public static final int textAppearancePopupMenuHeader = 16844033; // 0x1010501
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1380,6 +1386,11 @@
field public static final int tintMode = 16843771; // 0x10103fb
field public static final int title = 16843233; // 0x10101e1
field public static final int titleCondensed = 16843234; // 0x10101e2
+ field public static final int titleMargin = 16844023; // 0x10104f7
+ field public static final int titleMarginBottom = 16844027; // 0x10104fb
+ field public static final int titleMarginEnd = 16844025; // 0x10104f9
+ field public static final int titleMarginStart = 16844024; // 0x10104f8
+ field public static final int titleMarginTop = 16844026; // 0x10104fa
field public static final int titleTextAppearance = 16843822; // 0x101042e
field public static final int titleTextColor = 16844003; // 0x10104e3
field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -12640,7 +12651,6 @@
method public int getIntrinsicWidth();
method public int getLayoutDirection();
method public final int getLevel();
- method public final float getLevelFloat();
method public int getMinimumHeight();
method public int getMinimumWidth();
method public abstract int getOpacity();
@@ -12660,7 +12670,6 @@
method protected void onBoundsChange(android.graphics.Rect);
method public boolean onLayoutDirectionChanged(int);
method protected boolean onLevelChange(int);
- method protected boolean onLevelChange(float);
method protected boolean onStateChange(int[]);
method public static int resolveOpacity(int, int);
method public void scheduleSelf(java.lang.Runnable, long);
@@ -12678,15 +12687,12 @@
method public void setHotspotBounds(int, int, int, int);
method public final boolean setLayoutDirection(int);
method public final boolean setLevel(int);
- method public final boolean setLevel(float);
method public boolean setState(int[]);
method public void setTint(int);
method public void setTintList(android.content.res.ColorStateList);
method public void setTintMode(android.graphics.PorterDuff.Mode);
method public boolean setVisible(boolean, boolean);
method public void unscheduleSelf(java.lang.Runnable);
- field public static final int MAX_LEVEL = 10000; // 0x2710
- field public static final float MAX_LEVEL_FLOAT = 10000.0f;
}
public static abstract interface Drawable.Callback {
@@ -19639,6 +19645,7 @@
method public boolean importFile(int, java.lang.String);
method public boolean importFile(int, android.os.ParcelFileDescriptor);
method public boolean open(android.hardware.usb.UsbDeviceConnection);
+ method public android.mtp.MtpEvent readEvent(android.os.CancellationSignal);
method public boolean sendObject(int, int, android.os.ParcelFileDescriptor);
method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo);
}
@@ -19650,6 +19657,11 @@
method public final java.lang.String getVersion();
}
+ public class MtpEvent {
+ ctor public MtpEvent();
+ method public int getEventCode();
+ }
+
public final class MtpObjectInfo {
method public final int getAssociationDesc();
method public final int getAssociationType();
@@ -19759,6 +19771,7 @@
field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+ field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -20906,6 +20919,22 @@
field public byte id;
}
+ public class ScanInfo implements android.os.Parcelable {
+ ctor public ScanInfo(android.net.wifi.ScanResult);
+ ctor public ScanInfo(long, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int);
+ method public int describeContents();
+ method public long getBssid();
+ method public byte[] getIconData();
+ method public java.lang.String getIconType();
+ method public java.lang.String getName();
+ method public int getOsuIdentity();
+ method public int getRssi();
+ method public android.net.wifi.ScanResult getScanResult();
+ method public java.lang.String getServiceDescription();
+ method public java.lang.String getSsid();
+ method public void writeToParcel(android.os.Parcel, int);
+ }
+
public class ScanResult implements android.os.Parcelable {
method public int describeContents();
method public boolean is80211mcResponder();
@@ -21131,6 +21160,7 @@
method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics();
method public android.net.DhcpInfo getDhcpInfo();
method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
+ method public java.util.List<android.net.wifi.ScanInfo> getScanInfos();
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
method public int getWifiState();
method public boolean is5GHzBandSupported();
@@ -21150,6 +21180,7 @@
method public boolean reconnect();
method public boolean removeNetwork(int);
method public boolean saveConfiguration();
+ method public void setOsuSelection(int);
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
method public boolean setWifiEnabled(boolean);
@@ -25664,8 +25695,8 @@
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
method public int getIdentifier();
- method public final boolean isOwner();
- method public static final int myUserId();
+ method public boolean isOwner();
+ method public static int myUserId();
method public static android.os.UserHandle readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -32839,6 +32870,7 @@
field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
+ field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -37101,6 +37133,7 @@
method public int getAction();
method public android.content.ClipData getClipData();
method public android.content.ClipDescription getClipDescription();
+ method public android.view.DropPermissionHolder getDropPermissionHolder();
method public java.lang.Object getLocalState();
method public boolean getResult();
method public float getX();
@@ -37115,6 +37148,14 @@
field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
}
+ public class DropPermissionHolder implements android.os.Parcelable {
+ method public int describeContents();
+ method public void grant();
+ method public void revoke();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+ }
+
public class FocusFinder {
method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
@@ -38803,6 +38844,7 @@
method public void setY(float);
method public void setZ(float);
method public boolean showContextMenu();
+ method public boolean showContextMenu(float, float);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
method public void startAnimation(android.view.animation.Animation);
@@ -38818,6 +38860,11 @@
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+ field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
+ field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
+ field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
+ field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
+ field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
@@ -39264,6 +39311,7 @@
method public void setTransitionGroup(boolean);
method public boolean shouldDelayChildPressedState();
method public boolean showContextMenuForChild(android.view.View);
+ method public boolean showContextMenuForChild(android.view.View, float, float);
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
method public void startLayoutAnimation();
@@ -39385,6 +39433,7 @@
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View);
+ method public abstract boolean showContextMenuForChild(android.view.View, float, float);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
}
@@ -43477,6 +43526,7 @@
method public int getInputMethodMode();
method public int getMaxAvailableHeight(android.view.View);
method public int getMaxAvailableHeight(android.view.View, int);
+ method public int getMaxAvailableHeight(android.view.View, int, boolean);
method public boolean getOverlapAnchor();
method public int getSoftInputMode();
method public int getWidth();
@@ -43563,6 +43613,7 @@
method public void setInterpolator(android.view.animation.Interpolator);
method public synchronized void setMax(int);
method public synchronized void setProgress(int);
+ method public void setProgress(int, boolean);
method public void setProgressBackgroundTintList(android.content.res.ColorStateList);
method public void setProgressBackgroundTintMode(android.graphics.PorterDuff.Mode);
method public void setProgressDrawable(android.graphics.drawable.Drawable);
@@ -44559,6 +44610,10 @@
method public int getPopupTheme();
method public java.lang.CharSequence getSubtitle();
method public java.lang.CharSequence getTitle();
+ method public int getTitleMarginBottom();
+ method public int getTitleMarginEnd();
+ method public int getTitleMarginStart();
+ method public int getTitleMarginTop();
method public boolean hasExpandedActionView();
method public boolean hideOverflowMenu();
method public void inflateMenu(int);
@@ -44584,6 +44639,11 @@
method public void setSubtitleTextColor(int);
method public void setTitle(int);
method public void setTitle(java.lang.CharSequence);
+ method public void setTitleMargin(int, int, int, int);
+ method public void setTitleMarginBottom(int);
+ method public void setTitleMarginEnd(int);
+ method public void setTitleMarginStart(int);
+ method public void setTitleMarginTop(int);
method public void setTitleTextAppearance(android.content.Context, int);
method public void setTitleTextColor(int);
method public boolean showOverflowMenu();
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9709299..5c9fd51 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -19,6 +19,8 @@
package com.android.commands.am;
import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
+import static android.app.ActivityManager.RESIZE_MODE_USER;
import android.app.ActivityManager;
import android.app.ActivityManager.StackInfo;
@@ -161,6 +163,7 @@
" am task drag-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] \n" +
" am task size-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] \n" +
" am get-config\n" +
+ " am suppress-resize-config-changes <true|false>\n" +
" am set-inactive [--user <USER_ID>] <PACKAGE> true|false\n" +
" am get-inactive [--user <USER_ID>] <PACKAGE>\n" +
" am send-trim-memory [--user <USER_ID>] <PROCESS>\n" +
@@ -324,6 +327,8 @@
"\n" +
"am get-config: retrieve the configuration and any recent configurations\n" +
" of the device.\n" +
+ "am suppress-resize-config-changes: suppresses configuration changes due to\n" +
+ " user resizing an activity/task.\n" +
"\n" +
"am set-inactive: sets the inactive state of an app.\n" +
"\n" +
@@ -451,6 +456,8 @@
runTask();
} else if (op.equals("get-config")) {
runGetConfig();
+ } else if (op.equals("suppress-resize-config-changes")) {
+ runSuppressResizeConfigChanges();
} else if (op.equals("set-inactive")) {
runSetInactive();
} else if (op.equals("get-inactive")) {
@@ -2266,12 +2273,13 @@
System.err.println("Error: invalid input bounds");
return;
}
- taskResize(taskId, bounds, 0);
+ taskResize(taskId, bounds, 0, false);
}
- private void taskResize(int taskId, Rect bounds, int delay_ms) {
+ private void taskResize(int taskId, Rect bounds, int delay_ms, boolean pretendUserResize) {
try {
- mAm.resizeTask(taskId, bounds);
+ final int resizeMode = pretendUserResize ? RESIZE_MODE_USER : RESIZE_MODE_SYSTEM;
+ mAm.resizeTask(taskId, bounds, resizeMode);
Thread.sleep(delay_ms);
} catch (RemoteException e) {
System.err.println("Error changing task bounds: " + e);
@@ -2354,7 +2362,7 @@
taskRect.top += maxMove;
taskRect.bottom += maxMove;
}
- taskResize(taskId, taskRect, delay_ms);
+ taskResize(taskId, taskRect, delay_ms, false);
}
} else {
while (maxToTravel < 0
@@ -2371,7 +2379,7 @@
taskRect.top -= maxMove;
taskRect.bottom -= maxMove;
}
- taskResize(taskId, taskRect, delay_ms);
+ taskResize(taskId, taskRect, delay_ms, false);
}
}
// Return the remaining distance we didn't travel because we reached the target location.
@@ -2405,7 +2413,7 @@
currentTaskBounds.left -= getStepSize(
currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (stackBounds.top < currentTaskBounds.top
|| stackBounds.left < currentTaskBounds.left);
@@ -2418,7 +2426,7 @@
currentTaskBounds.left += getStepSize(
currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (initialTaskBounds.top > currentTaskBounds.top
|| initialTaskBounds.left > currentTaskBounds.left);
@@ -2431,7 +2439,7 @@
currentTaskBounds.right += getStepSize(
currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (stackBounds.top < currentTaskBounds.top
|| stackBounds.right > currentTaskBounds.right);
@@ -2444,7 +2452,7 @@
currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
stepSize, GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (initialTaskBounds.top > currentTaskBounds.top
|| initialTaskBounds.right < currentTaskBounds.right);
@@ -2457,7 +2465,7 @@
currentTaskBounds.left -= getStepSize(
currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (stackBounds.bottom > currentTaskBounds.bottom
|| stackBounds.left < currentTaskBounds.left);
@@ -2470,7 +2478,7 @@
currentTaskBounds.left += getStepSize(
currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (initialTaskBounds.bottom < currentTaskBounds.bottom
|| initialTaskBounds.left > currentTaskBounds.left);
@@ -2483,7 +2491,7 @@
currentTaskBounds.right += getStepSize(
currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (stackBounds.bottom > currentTaskBounds.bottom
|| stackBounds.right > currentTaskBounds.right);
@@ -2496,7 +2504,7 @@
currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
stepSize, GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (initialTaskBounds.bottom < currentTaskBounds.bottom
|| initialTaskBounds.right < currentTaskBounds.right);
}
@@ -2603,6 +2611,16 @@
}
}
+ private void runSuppressResizeConfigChanges() throws Exception {
+ boolean suppress = Boolean.valueOf(nextArgRequired());
+
+ try {
+ mAm.suppressResizeConfigChanges(suppress);
+ } catch (RemoteException e) {
+ System.err.println("Error suppressing resize config changes: " + e);
+ }
+ }
+
private void runSetInactive() throws Exception {
int userId = UserHandle.USER_CURRENT;
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 3f0a444..ebf5085 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -22,11 +22,13 @@
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
+import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
@@ -92,6 +94,7 @@
IPackageManager mPm;
IPackageInstaller mInstaller;
IUserManager mUm;
+ IAccountManager mAm;
private WeakHashMap<String, Resources> mResourceCache
= new WeakHashMap<String, Resources>();
@@ -122,9 +125,10 @@
if (args.length < 1) {
return showUsage();
}
-
- mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
+ mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE));
+ mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE));
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+
if (mPm == null) {
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
@@ -1381,6 +1385,8 @@
}
} else if ("--managed".equals(opt)) {
flags |= UserInfo.FLAG_MANAGED_PROFILE;
+ } else if ("--restricted".equals(opt)) {
+ flags |= UserInfo.FLAG_RESTRICTED;
} else {
System.err.println("Error: unknown option " + opt);
showUsage();
@@ -1394,12 +1400,18 @@
}
name = arg;
try {
- UserInfo info = null;
- if (userId < 0) {
+ UserInfo info;
+ if ((flags & UserInfo.FLAG_RESTRICTED) != 0) {
+ // In non-split user mode, userId can only be SYSTEM
+ int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
+ info = mUm.createRestrictedProfile(name, parentUserId);
+ mAm.addSharedAccountsFromParentUser(userId, parentUserId);
+ } else if (userId < 0) {
info = mUm.createUser(name, flags);
} else {
info = mUm.createProfileForUser(name, flags, userId);
}
+
if (info != null) {
System.out.println("Success: created user id " + info.id);
return 1;
@@ -2122,7 +2134,7 @@
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
System.err.println(" pm trim-caches DESIRED_FREE_SPACE [internal|UUID]");
- System.err.println(" pm create-user [--profileOf USER_ID] [--managed] USER_NAME");
+ System.err.println(" pm create-user [--profileOf USER_ID] [--managed] [--restricted] USER_NAME");
System.err.println(" pm remove-user USER_ID");
System.err.println(" pm get-max-users");
System.err.println("");
diff --git a/cmds/svc/src/com/android/commands/svc/NfcCommand.java b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
new file mode 100644
index 0000000..e0f09ee
--- /dev/null
+++ b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.svc;
+
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.nfc.INfcAdapter;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+public class NfcCommand extends Svc.Command {
+
+ public NfcCommand() {
+ super("nfc");
+ }
+
+ @Override
+ public String shortHelp() {
+ return "Control NFC functions";
+ }
+
+ @Override
+ public String longHelp() {
+ return shortHelp() + "\n"
+ + "\n"
+ + "usage: svc nfc [enable|disable]\n"
+ + " Turn NFC on or off.\n\n";
+ }
+
+ @Override
+ public void run(String[] args) {
+ boolean validCommand = false;
+ if (args.length >= 2) {
+ boolean flag = false;
+ if ("enable".equals(args[1])) {
+ flag = true;
+ validCommand = true;
+ } else if ("disable".equals(args[1])) {
+ flag = false;
+ validCommand = true;
+ }
+ if (validCommand) {
+ IPackageManager pm = IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package"));
+ try {
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
+ INfcAdapter nfc = INfcAdapter.Stub
+ .asInterface(ServiceManager.getService(Context.NFC_SERVICE));
+ try {
+ if (flag) {
+ nfc.enable();
+ } else
+ nfc.disable(true);
+ } catch (RemoteException e) {
+ System.err.println("NFC operation failed: " + e);
+ }
+ } else {
+ System.err.println("NFC feature not supported.");
+ }
+ } catch (RemoteException e) {
+ System.err.println("RemoteException while calling PackageManager, is the "
+ + "system running?");
+ }
+ return;
+ }
+ }
+ System.err.println(longHelp());
+ }
+
+}
diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java
index 0fbba11..2cccd1a 100644
--- a/cmds/svc/src/com/android/commands/svc/Svc.java
+++ b/cmds/svc/src/com/android/commands/svc/Svc.java
@@ -95,6 +95,7 @@
new PowerCommand(),
new DataCommand(),
new WifiCommand(),
- new UsbCommand()
+ new UsbCommand(),
+ new NfcCommand(),
};
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index a475041..0a7568a 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,6 +16,7 @@
package android.accounts;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.Size;
import android.app.Activity;
@@ -423,10 +424,11 @@
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @NonNull
@RequiresPermission(GET_ACCOUNTS)
public Account[] getAccounts() {
try {
- return mService.getAccounts(null);
+ return mService.getAccounts(null, mContext.getOpPackageName());
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
@@ -448,10 +450,11 @@
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @NonNull
@RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsAsUser(int userId) {
try {
- return mService.getAccountsAsUser(null, userId);
+ return mService.getAccountsAsUser(null, userId, mContext.getOpPackageName());
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
@@ -466,9 +469,10 @@
* @param uid the uid of the calling app.
* @return the accounts that are available to this package and user.
*/
+ @NonNull
public Account[] getAccountsForPackage(String packageName, int uid) {
try {
- return mService.getAccountsForPackage(packageName, uid);
+ return mService.getAccountsForPackage(packageName, uid, mContext.getOpPackageName());
} catch (RemoteException re) {
// won't ever happen
throw new RuntimeException(re);
@@ -483,9 +487,11 @@
* @return An array of {@link Account}, one per matching account. Empty
* (never null) if no accounts of the specified type have been added.
*/
+ @NonNull
public Account[] getAccountsByTypeForPackage(String type, String packageName) {
try {
- return mService.getAccountsByTypeForPackage(type, packageName);
+ return mService.getAccountsByTypeForPackage(type, packageName,
+ mContext.getOpPackageName());
} catch (RemoteException re) {
// won't ever happen
throw new RuntimeException(re);
@@ -514,15 +520,18 @@
* @return An array of {@link Account}, one per matching account. Empty
* (never null) if no accounts of the specified type have been added.
*/
+ @NonNull
@RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsByType(String type) {
return getAccountsByTypeAsUser(type, Process.myUserHandle());
}
/** @hide Same as {@link #getAccountsByType(String)} but for a specific user. */
+ @NonNull
public Account[] getAccountsByTypeAsUser(String type, UserHandle userHandle) {
try {
- return mService.getAccountsAsUser(type, userHandle.getIdentifier());
+ return mService.getAccountsAsUser(type, userHandle.getIdentifier(),
+ mContext.getOpPackageName());
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
@@ -610,7 +619,7 @@
if (features == null) throw new IllegalArgumentException("features is null");
return new Future2Task<Boolean>(handler, callback) {
public void doWork() throws RemoteException {
- mService.hasFeatures(mResponse, account, features);
+ mService.hasFeatures(mResponse, account, features, mContext.getOpPackageName());
}
public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
@@ -662,7 +671,8 @@
if (type == null) throw new IllegalArgumentException("type is null");
return new Future2Task<Account[]>(handler, callback) {
public void doWork() throws RemoteException {
- mService.getAccountsByFeatures(mResponse, type, features);
+ mService.getAccountsByFeatures(mResponse, type, features,
+ mContext.getOpPackageName());
}
public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException {
if (!bundle.containsKey(KEY_ACCOUNTS)) {
@@ -1534,23 +1544,22 @@
}.start();
}
+
/**
- * Adds a shared account from the primary user to a secondary user. Adding the shared account
+ * Adds shared accounts from a parent user to a secondary user. Adding the shared account
* doesn't take effect immediately. When the target user starts up, any pending shared accounts
* are attempted to be copied to the target user from the primary via calls to the
* authenticator.
- * @param account the account to share
- * @param user the target user
- * @return
+ * @param parentUser parent user
+ * @param user target user
* @hide
*/
- public boolean addSharedAccount(final Account account, UserHandle user) {
+ public void addSharedAccountsFromParentUser(UserHandle parentUser, UserHandle user) {
try {
- boolean val = mService.addSharedAccountAsUser(account, user.getIdentifier());
- return val;
+ mService.addSharedAccountsFromParentUser(parentUser.getIdentifier(),
+ user.getIdentifier());
} catch (RemoteException re) {
- // won't ever happen
- throw new RuntimeException(re);
+ throw new IllegalStateException(re);
}
}
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 04b3c88..0d95db1 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -30,12 +30,14 @@
String getPassword(in Account account);
String getUserData(in Account account, String key);
AuthenticatorDescription[] getAuthenticatorTypes(int userId);
- Account[] getAccounts(String accountType);
- Account[] getAccountsForPackage(String packageName, int uid);
- Account[] getAccountsByTypeForPackage(String type, String packageName);
- Account[] getAccountsAsUser(String accountType, int userId);
- void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features);
- void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
+ Account[] getAccounts(String accountType, String opPackageName);
+ Account[] getAccountsForPackage(String packageName, int uid, String opPackageName);
+ Account[] getAccountsByTypeForPackage(String type, String packageName, String opPackageName);
+ Account[] getAccountsAsUser(String accountType, int userId, String opPackageName);
+ void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features,
+ String opPackageName);
+ void getAccountsByFeatures(in IAccountManagerResponse response, String accountType,
+ in String[] features, String opPackageName);
boolean addAccountExplicitly(in Account account, String password, in Bundle extras);
void removeAccount(in IAccountManagerResponse response, in Account account,
boolean expectActivityLaunch);
@@ -72,9 +74,9 @@
String authTokenType);
/* Shared accounts */
- boolean addSharedAccountAsUser(in Account account, int userId);
Account[] getSharedAccountsAsUser(int userId);
boolean removeSharedAccountAsUser(in Account account, int userId);
+ void addSharedAccountsFromParentUser(int parentUserId, int userId);
/* Account renaming. */
void renameAccount(in IAccountManagerResponse response, in Account accountToRename, String newName);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 73c3786..4997dc7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2736,40 +2736,6 @@
}
/**
- * Returns the bounds of the task that contains this activity.
- *
- * @return Rect The bounds that contains the activity.
- * @hide
- */
- @Override
- public Rect getActivityBounds() throws RemoteException {
- return ActivityManagerNative.getDefault().getActivityBounds(mToken);
- }
-
- /**
- * Sets the bounds (size and position) of the task or stack that contains this
- * activity.
- * NOTE: The requested bounds might not the fully honored by the system depending
- * on the window placement policy.
- *
- * @param newBounds The new target bounds of the activity in task or stack.
- * @hide
- */
- @Override
- public void setActivityBounds(Rect newBounds) throws RemoteException {
- ActivityManagerNative.getDefault().setActivityBounds(mToken, newBounds);
- }
-
- /**
- * Activates this activity, hence bringing it to the top and giving it focus.
- * Note: This will only work for activities which are located on the freeform desktop.
- * @hide
- */
- public void activateActivity() throws RemoteException {
- ActivityManagerNative.getDefault().activateActivity(mToken);
- }
-
- /**
* Called to process key events. You can override this to intercept all
* key events before they are dispatched to the window. Be sure to call
* this implementation for key events that should be handled normally.
@@ -3839,6 +3805,12 @@
* #checkSelfPermission(String)}.
* </p>
* <p>
+ * Calling this API for permissions already granted to your app would show UI
+ * to the user to decide whether the app can still hold these permissions. This
+ * can be useful if the way your app uses data guarded by the permissions
+ * changes significantly.
+ * </p>
+ * <p>
* You cannot request a permission if your activity sets {@link
* android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
* <code>true</code> because in this case the activity would not receive
@@ -4907,7 +4879,8 @@
if (Looper.myLooper() != mMainThread.getLooper()) {
throw new IllegalStateException("Must be called from main thread");
}
- mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, null, false);
+ mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, null, false,
+ false /* preserveWindow */);
}
/**
@@ -6251,12 +6224,13 @@
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
- Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
+ Configuration config, String referrer, IVoiceInteractor voiceInteractor,
+ Window window) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
- mWindow = new PhoneWindow(this);
+ mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
@@ -6345,11 +6319,15 @@
final void performRestart() {
mFragments.noteStateNotSaved();
+ if (mToken != null && mParent == null) {
+ // We might have view roots that were preserved during a relaunch, we need to start them
+ // again. We don't need to check mStopped, the roots will check if they were actually
+ // stopped.
+ WindowManagerGlobal.getInstance().setStoppedState(mToken, false /* stopped */);
+ }
+
if (mStopped) {
mStopped = false;
- if (mToken != null && mParent == null) {
- WindowManagerGlobal.getInstance().setStoppedState(mToken, false);
- }
synchronized (mManagedCursors) {
final int N = mManagedCursors.size();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index eeae20f..6606f9b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -452,6 +452,53 @@
*/
public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
+ /**
+ * Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
+ * specifies the position of the created docked stack at the top half of the screen if
+ * in portrait mode or at the left half of the screen if in landscape mode.
+ * @hide
+ */
+ public static final int DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT = 0;
+
+ /**
+ * Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
+ * specifies the position of the created docked stack at the bottom half of the screen if
+ * in portrait mode or at the right half of the screen if in landscape mode.
+ * @hide
+ */
+ public static final int DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
+
+
+ /**
+ * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
+ * that the resize is from the window manager (instead of the user).
+ * @hide
+ */
+ public static final int RESIZE_MODE_SYSTEM = 0;
+
+ /**
+ * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
+ * that the resize is from the window manager (instead of the user) due to a screen
+ * rotation change.
+ * @hide
+ */
+ public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = 1;
+
+ /**
+ * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
+ * that the resize is initiated by the user (most likely via a drag action on the
+ * window's edge or corner).
+ * @hide
+ */
+ public static final int RESIZE_MODE_USER = 2;
+
+ /**
+ * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
+ * that the resize should be performed even if the bounds appears unchanged.
+ * @hide
+ */
+ public static final int RESIZE_MODE_FORCED = 3;
+
/** @hide */
public int getFrontActivityScreenCompatMode() {
try {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1f1f356..cb1a89f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -743,6 +743,16 @@
return true;
}
+ case MOVE_TASK_TO_DOCKED_STACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int taskId = data.readInt();
+ int createMode = data.readInt();
+ boolean toTop = data.readInt() != 0;
+ moveTaskToDockedStack(taskId, createMode, toTop);
+ reply.writeNoException();
+ return true;
+ }
+
case RESIZE_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int stackId = data.readInt();
@@ -752,24 +762,6 @@
return true;
}
- case GET_ACTIVITY_BOUNDS_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- IBinder token = data.readStrongBinder();
- Rect r = getActivityBounds(token);
- reply.writeNoException();
- r.writeToParcel(reply, 0);
- return true;
- }
-
- case SET_ACTIVITY_BOUNDS_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- IBinder token = data.readStrongBinder();
- Rect r = Rect.CREATOR.createFromParcel(data);
- setActivityBounds(token, r);
- reply.writeNoException();
- return true;
- }
-
case POSITION_TASK_IN_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int taskId = data.readInt();
@@ -827,14 +819,6 @@
return true;
}
- case ACTIVATE_ACTIVITY_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- IBinder token = data.readStrongBinder();
- activateActivity(token);
- reply.writeNoException();
- return true;
- }
-
case SET_FOCUSED_TASK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int taskId = data.readInt();
@@ -2468,8 +2452,9 @@
case RESIZE_TASK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int taskId = data.readInt();
+ int resizeMode = data.readInt();
Rect r = Rect.CREATOR.createFromParcel(data);
- resizeTask(taskId, r);
+ resizeTask(taskId, r, resizeMode);
reply.writeNoException();
return true;
}
@@ -2697,6 +2682,13 @@
reportSizeConfigurations(token, horizontal, vertical);
return true;
}
+ case SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final boolean suppress = data.readInt() == 1;
+ suppressResizeConfigChanges(suppress);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -3536,6 +3528,21 @@
reply.recycle();
}
@Override
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ data.writeInt(createMode);
+ data.writeInt(toTop ? 1 : 0);
+ mRemote.transact(MOVE_TASK_TO_DOCKED_STACK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ @Override
public void resizeStack(int stackId, Rect r) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -3631,18 +3638,6 @@
return focusedStackId;
}
@Override
- public void activateActivity(IBinder token) throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(token);
- mRemote.transact(ACTIVATE_ACTIVITY_TRANSACTION, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
- @Override
public void setFocusedTask(int taskId) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -5912,12 +5907,13 @@
}
@Override
- public void resizeTask(int taskId, Rect r) throws RemoteException
+ public void resizeTask(int taskId, Rect r, int resizeMode) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(taskId);
+ data.writeInt(resizeMode);
r.writeToParcel(data, 0);
mRemote.transact(RESIZE_TASK_TRANSACTION, data, reply, 0);
reply.readException();
@@ -5941,35 +5937,6 @@
}
@Override
- public void setActivityBounds(IBinder token, Rect r) throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(token);
- r.writeToParcel(data, 0);
- mRemote.transact(SET_ACTIVITY_BOUNDS_TRANSACTION, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
-
- @Override
- public Rect getActivityBounds(IBinder token) throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(token);
- mRemote.transact(GET_ACTIVITY_BOUNDS_TRANSACTION, data, reply, 0);
- reply.readException();
- Rect rect = Rect.CREATOR.createFromParcel(reply);
- data.recycle();
- reply.recycle();
- return rect;
- }
-
- @Override
public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -6256,5 +6223,17 @@
reply.recycle();
}
+ @Override
+ public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(suppress ? 1 : 0);
+ mRemote.transact(SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 67dee7f..4b8efab 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -315,8 +315,9 @@
int pendingConfigChanges;
boolean onlyLocalRequest;
- View mPendingRemoveWindow;
+ Window mPendingRemoveWindow;
WindowManager mPendingRemoveWindowManager;
+ boolean mPreserveWindow;
ActivityClientRecord() {
parent = null;
@@ -670,9 +671,9 @@
public final void scheduleRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig) {
+ Configuration overrideConfig, boolean preserveWindow) {
requestRelaunchActivity(token, pendingResults, pendingNewIntents,
- configChanges, notResumed, config, overrideConfig, true);
+ configChanges, notResumed, config, overrideConfig, true, preserveWindow);
}
public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) {
@@ -2376,10 +2377,16 @@
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
+ Window window = null;
+ if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
+ window = r.mPendingRemoveWindow;
+ r.mPendingRemoveWindow = null;
+ r.mPendingRemoveWindowManager = null;
+ }
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
- r.referrer, r.voiceInteractor);
+ r.referrer, r.voiceInteractor, window);
if (customIntent != null) {
activity.mIntent = customIntent;
@@ -3191,10 +3198,14 @@
return r;
}
- static final void cleanUpPendingRemoveWindows(ActivityClientRecord r) {
+ static final void cleanUpPendingRemoveWindows(ActivityClientRecord r, boolean force) {
+ if (r.mPreserveWindow && !force) {
+ return;
+ }
if (r.mPendingRemoveWindow != null) {
- r.mPendingRemoveWindowManager.removeViewImmediate(r.mPendingRemoveWindow);
- IBinder wtoken = r.mPendingRemoveWindow.getWindowToken();
+ r.mPendingRemoveWindowManager.removeViewImmediate(
+ r.mPendingRemoveWindow.getDecorView());
+ IBinder wtoken = r.mPendingRemoveWindow.getDecorView().getWindowToken();
if (wtoken != null) {
WindowManagerGlobal.getInstance().closeAll(wtoken,
r.activity.getClass().getName(), "Activity");
@@ -3245,7 +3256,11 @@
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
- if (a.mVisibleFromClient) {
+ if (r.mPreserveWindow) {
+ a.mWindowAdded = true;
+ r.mPreserveWindow = false;
+ }
+ if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
@@ -3260,7 +3275,7 @@
}
// Get rid of anything left hanging around.
- cleanUpPendingRemoveWindows(r);
+ cleanUpPendingRemoveWindows(r, false /* force */);
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
@@ -3745,7 +3760,8 @@
// request all activities to relaunch for the changes to take place
for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
- requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false);
+ requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false,
+ false /* preserveWindow */);
}
}
}
@@ -3931,7 +3947,7 @@
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance);
if (r != null) {
- cleanUpPendingRemoveWindows(r);
+ cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
if (v != null) {
@@ -3940,11 +3956,18 @@
}
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
- if (r.onlyLocalRequest) {
+ boolean reuseForResize = r.window.hasNonClientDecorView() && r.mPreserveWindow;
+ if (r.onlyLocalRequest || reuseForResize) {
// Hold off on removing this until the new activity's
// window is being added.
- r.mPendingRemoveWindow = v;
+ r.mPendingRemoveWindow = r.window;
r.mPendingRemoveWindowManager = wm;
+ if (reuseForResize) {
+ // We can only keep the part of the view hierarchy that we control,
+ // everything else must be removed, because it might not be able to
+ // behave properly when activity is relaunching.
+ r.window.clearContentView();
+ }
} else {
wm.removeViewImmediate(v);
}
@@ -3986,10 +4009,14 @@
mSomeActivitiesChanged = true;
}
+ /**
+ * @param preserveWindow Whether the activity should try to reuse the window it created,
+ * including the decor view after the relaunch.
+ */
public final void requestRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig, boolean fromServer) {
+ Configuration overrideConfig, boolean fromServer, boolean preserveWindow) {
ActivityClientRecord target = null;
synchronized (mResourcesManager) {
@@ -4020,6 +4047,7 @@
target.token = token;
target.pendingResults = pendingResults;
target.pendingIntents = pendingNewIntents;
+ target.mPreserveWindow = preserveWindow;
if (!fromServer) {
ActivityClientRecord existing = mActivities.get(token);
if (existing != null) {
@@ -4120,6 +4148,7 @@
r.activity.mConfigChangeFlags |= configChanges;
r.onlyLocalRequest = tmp.onlyLocalRequest;
+ r.mPreserveWindow = tmp.mPreserveWindow;
Intent currentIntent = r.activity.mIntent;
r.activity.mChangingConfigurations = true;
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 5c6fe46..38562da 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -150,7 +150,13 @@
}
public void setEnterActivityOptions(Activity activity, ActivityOptions options) {
- if (activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
+ final Window window = activity.getWindow();
+ if (window == null) {
+ return;
+ }
+ // ensure Decor View has been created so that the window features are activated
+ window.getDecorView();
+ if (window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
&& options != null && mEnterActivityOptions == null
&& mEnterTransitionCoordinator == null
&& options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 9c0d931..371c923 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -239,6 +239,8 @@
}
mTextureView.setSurfaceTextureListener(null);
+
+ mThread.quit();
}
private void attachToSurfaceWhenReady() {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 42ac67c..09c0a6e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -235,8 +235,10 @@
public static final int OP_WRITE_EXTERNAL_STORAGE = 60;
/** @hide Turned on the screen. */
public static final int OP_TURN_SCREEN_ON = 61;
+ /** @hide Get device accounts. */
+ public static final int OP_GET_ACCOUNTS = 62;
/** @hide */
- public static final int _NUM_OP = 62;
+ public static final int _NUM_OP = 63;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -331,6 +333,9 @@
/** Required to write/modify/update system settingss. */
public static final String OPSTR_WRITE_SETTINGS
= "android:write_settings";
+ /** @hide Get device accounts. */
+ public static final String OPSTR_GET_ACCOUNTS
+ = "android:get_accounts";
/**
* This maps each operation to the operation that serves as the
@@ -403,6 +408,7 @@
OP_READ_EXTERNAL_STORAGE,
OP_WRITE_EXTERNAL_STORAGE,
OP_TURN_SCREEN_ON,
+ OP_GET_ACCOUNTS,
};
/**
@@ -472,6 +478,7 @@
OPSTR_READ_EXTERNAL_STORAGE,
OPSTR_WRITE_EXTERNAL_STORAGE,
null,
+ OPSTR_GET_ACCOUNTS
};
/**
@@ -541,6 +548,7 @@
"READ_EXTERNAL_STORAGE",
"WRITE_EXTERNAL_STORAGE",
"TURN_ON_SCREEN",
+ "GET_ACCOUNTS",
};
/**
@@ -610,6 +618,7 @@
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
null, // no permission for turning the screen on
+ Manifest.permission.GET_ACCOUNTS
};
/**
@@ -680,6 +689,7 @@
null, // READ_EXTERNAL_STORAGE
null, // WRITE_EXTERNAL_STORAGE
null, // TURN_ON_SCREEN
+ null, // GET_ACCOUNTS
};
/**
@@ -749,6 +759,7 @@
false, // READ_EXTERNAL_STORAGE
false, // WRITE_EXTERNAL_STORAGE
false, // TURN_ON_SCREEN
+ false, // GET_ACCOUNTS
};
/**
@@ -817,6 +828,7 @@
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN
+ AppOpsManager.MODE_ALLOWED,
};
/**
@@ -889,6 +901,7 @@
false,
false,
false,
+ false
};
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0cd02dd..0d53dbe 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -93,8 +93,8 @@
import java.util.Map;
import java.util.Objects;
-/*package*/
-final class ApplicationPackageManager extends PackageManager {
+/** @hide */
+public class ApplicationPackageManager extends PackageManager {
private static final String TAG = "ApplicationPackageManager";
private final static boolean DEBUG_ICONS = false;
@@ -126,8 +126,14 @@
@Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
+ return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
+ }
+
+ @Override
+ public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+ throws NameNotFoundException {
try {
- PackageInfo pi = mPM.getPackageInfo(packageName, flags, mContext.getUserId());
+ PackageInfo pi = mPM.getPackageInfo(packageName, flags, userId);
if (pi != null) {
return pi;
}
@@ -1338,7 +1344,7 @@
final VerificationParams verificationParams = new VerificationParams(null, null,
null, VerificationParams.NO_UID, null);
installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
- installerPackageName, verificationParams, null);
+ installerPackageName, verificationParams, null, UserHandle.myUserId());
}
@Override
@@ -1348,7 +1354,7 @@
final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
null, VerificationParams.NO_UID, manifestDigest);
installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
- installerPackageName, verificationParams, encryptionParams);
+ installerPackageName, verificationParams, encryptionParams, UserHandle.myUserId());
}
@Override
@@ -1356,15 +1362,23 @@
IPackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
- installerPackageName, verificationParams, encryptionParams);
+ installerPackageName, verificationParams, encryptionParams, UserHandle.myUserId());
}
@Override
public void installPackage(Uri packageURI, PackageInstallObserver observer,
int flags, String installerPackageName) {
+ installPackageAsUser(packageURI, observer, flags, installerPackageName,
+ UserHandle.myUserId());
+ }
+
+ @Override
+ public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer, int flags,
+ String installerPackageName, int userId) {
final VerificationParams verificationParams = new VerificationParams(null, null,
null, VerificationParams.NO_UID, null);
- installCommon(packageURI, observer, flags, installerPackageName, verificationParams, null);
+ installCommon(packageURI, observer, flags, installerPackageName, verificationParams, null,
+ userId);
}
@Override
@@ -1375,7 +1389,7 @@
final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
null, VerificationParams.NO_UID, manifestDigest);
installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
- encryptionParams);
+ encryptionParams, UserHandle.myUserId());
}
@Override
@@ -1383,12 +1397,13 @@
PackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
- encryptionParams);
+ encryptionParams, UserHandle.myUserId());
}
private void installCommon(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
- VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
+ VerificationParams verificationParams, ContainerEncryptionParams encryptionParams,
+ int userId) {
if (!"file".equals(packageURI.getScheme())) {
throw new UnsupportedOperationException("Only file:// URIs are supported");
}
@@ -1398,17 +1413,22 @@
final String originPath = packageURI.getPath();
try {
- mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
- verificationParams, null);
+ mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
+ verificationParams, null, userId);
} catch (RemoteException ignored) {
}
}
@Override
- public int installExistingPackage(String packageName)
+ public int installExistingPackage(String packageName) throws NameNotFoundException {
+ return installExistingPackageAsUser(packageName, UserHandle.myUserId());
+ }
+
+ @Override
+ public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
try {
- int res = mPM.installExistingPackageAsUser(packageName, UserHandle.myUserId());
+ int res = mPM.installExistingPackageAsUser(packageName, userId);
if (res == INSTALL_FAILED_INVALID_URI) {
throw new NameNotFoundException("Package " + packageName + " doesn't exist");
}
@@ -1706,8 +1726,14 @@
@Override
public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
+ deletePackageAsUser(packageName, observer, flags, UserHandle.myUserId());
+ }
+
+ @Override
+ public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
+ int userId) {
try {
- mPM.deletePackageAsUser(packageName, observer, UserHandle.myUserId(), flags);
+ mPM.deletePackageAsUser(packageName, observer, userId, flags);
} catch (RemoteException e) {
// Should never happen!
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index f164a0a..bead625 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -63,14 +63,14 @@
if (in != null) {
return in;
}
-
+
return new ApplicationThreadProxy(obj);
}
-
+
public ApplicationThreadNative() {
attachInterface(this, descriptor);
}
-
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -96,7 +96,7 @@
scheduleStopActivity(b, show, configChanges);
return true;
}
-
+
case SCHEDULE_WINDOW_VISIBILITY_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -125,7 +125,7 @@
scheduleResumeActivity(b, procState, isForward, resumeArgs);
return true;
}
-
+
case SCHEDULE_SEND_RESULT_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -179,7 +179,9 @@
if (data.readInt() != 0) {
overrideConfig = Configuration.CREATOR.createFromParcel(data);
}
- scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig);
+ boolean preserveWindows = data.readInt() == 1;
+ scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig,
+ preserveWindows);
return true;
}
@@ -201,7 +203,7 @@
scheduleDestroyActivity(b, finishing, configChanges);
return true;
}
-
+
case SCHEDULE_RECEIVER_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -371,7 +373,7 @@
}
return true;
}
-
+
case DUMP_PROVIDER_TRANSACTION: {
data.enforceInterface(IApplicationThread.descriptor);
ParcelFileDescriptor fd = data.readFileDescriptor();
@@ -731,15 +733,15 @@
class ApplicationThreadProxy implements IApplicationThread {
private final IBinder mRemote;
-
+
public ApplicationThreadProxy(IBinder remote) {
mRemote = remote;
}
-
+
public final IBinder asBinder() {
return mRemote;
}
-
+
public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
Parcel data = Parcel.obtain();
@@ -856,7 +858,7 @@
public final void scheduleRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig) throws RemoteException {
+ Configuration overrideConfig, boolean preserveWindow) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
@@ -871,6 +873,7 @@
} else {
data.writeInt(0);
}
+ data.writeInt(preserveWindow ? 1 : 0);
mRemote.transact(SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
@@ -898,7 +901,7 @@
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String resultData,
Bundle map, boolean sync, int sendingUser, int processState) throws RemoteException {
@@ -940,7 +943,7 @@
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public final void scheduleCreateService(IBinder token, ServiceInfo info,
CompatibilityInfo compatInfo, int processState) throws RemoteException {
Parcel data = Parcel.obtain();
@@ -1055,7 +1058,7 @@
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public final void scheduleExit() throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -1128,7 +1131,7 @@
mRemote.transact(DUMP_SERVICE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public void dumpProvider(FileDescriptor fd, IBinder token, String[] args)
throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 84cbea9..9081ef8 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -1078,8 +1078,8 @@
*/
private ArrayList<View> addTransitionTargets(final TransitionState state,
final Transition enterTransition, final TransitionSet sharedElementTransition,
- final Transition overallTransition, final View container,
- final Fragment inFragment, final Fragment outFragment,
+ final Transition exitTransition, final Transition overallTransition,
+ final View container, final Fragment inFragment, final Fragment outFragment,
final ArrayList<View> hiddenFragmentViews, final boolean isBack,
final ArrayList<View> sharedElementTargets) {
if (enterTransition == null && sharedElementTransition == null &&
@@ -1103,6 +1103,13 @@
if (sharedElementTransition != null) {
namedViews = mapSharedElementsIn(state, isBack, inFragment);
removeTargets(sharedElementTransition, sharedElementTargets);
+ // keep the nonExistentView as excluded so the list doesn't get emptied
+ sharedElementTargets.remove(state.nonExistentView);
+ excludeViews(exitTransition, sharedElementTransition,
+ sharedElementTargets, false);
+ excludeViews(enterTransition, sharedElementTransition,
+ sharedElementTargets, false);
+
setSharedElementTargets(sharedElementTransition,
state.nonExistentView, namedViews, sharedElementTargets);
@@ -1126,6 +1133,12 @@
}
setSharedElementEpicenter(enterTransition, state);
}
+
+ excludeViews(exitTransition, enterTransition, enteringViews, true);
+ excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
+ true);
+ excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
+ true);
return true;
}
});
@@ -1279,6 +1292,9 @@
if (exitingViews == null || exitingViews.isEmpty()) {
exitTransition = null;
}
+ excludeViews(enterTransition, exitTransition, exitingViews, true);
+ excludeViews(enterTransition, sharedElementTransition, sharedElementTargets, true);
+ excludeViews(exitTransition, sharedElementTransition, sharedElementTargets, true);
// Set the epicenter of the exit transition
if (mSharedElementTargetNames != null && namedViews != null) {
@@ -1299,7 +1315,7 @@
if (transition != null) {
ArrayList<View> hiddenFragments = new ArrayList<View>();
ArrayList<View> enteringViews = addTransitionTargets(state, enterTransition,
- sharedElementTransition, transition, sceneRoot, inFragment,
+ sharedElementTransition, exitTransition, transition, sceneRoot, inFragment,
outFragment, hiddenFragments, isBack, sharedElementTargets);
transition.setNameOverrides(state.nameOverrides);
@@ -1379,6 +1395,16 @@
return false;
}
+ private static void excludeViews(Transition transition, Transition fromTransition,
+ ArrayList<View> views, boolean exclude) {
+ if (transition != null) {
+ final int viewCount = fromTransition == null ? 0 : views.size();
+ for (int i = 0; i < viewCount; i++) {
+ transition.excludeTarget(views.get(i), exclude);
+ }
+ }
+ }
+
/**
* After the transition has started, remove all targets that we added to the transitions
* so that the transitions are left in a clean state.
@@ -1396,9 +1422,15 @@
sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
if (enterTransition != null) {
removeTargets(enterTransition, enteringViews);
+ excludeViews(enterTransition, exitTransition, exitingViews, false);
+ excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
+ false);
}
if (exitTransition != null) {
removeTargets(exitTransition, exitingViews);
+ excludeViews(exitTransition, enterTransition, enteringViews, false);
+ excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
+ false);
}
if (sharedElementTransition != null) {
removeTargets(sharedElementTransition, sharedElementTargets);
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 82206ea..b44aab7 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -63,6 +63,7 @@
final boolean mRetainInstance;
final boolean mDetached;
final Bundle mArguments;
+ final boolean mHidden;
Bundle mSavedFragmentState;
@@ -78,6 +79,7 @@
mRetainInstance = frag.mRetainInstance;
mDetached = frag.mDetached;
mArguments = frag.mArguments;
+ mHidden = frag.mHidden;
}
public FragmentState(Parcel in) {
@@ -90,6 +92,7 @@
mRetainInstance = in.readInt() != 0;
mDetached = in.readInt() != 0;
mArguments = in.readBundle();
+ mHidden = in.readInt() != 0;
mSavedFragmentState = in.readBundle();
}
@@ -117,6 +120,7 @@
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
+ mInstance.mHidden = mHidden;
mInstance.mFragmentManager = host.mFragmentManager;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment " + mInstance);
@@ -138,6 +142,7 @@
dest.writeInt(mRetainInstance ? 1 : 0);
dest.writeInt(mDetached ? 1 : 0);
dest.writeBundle(mArguments);
+ dest.writeInt(mHidden ? 1 : 0);
dest.writeBundle(mSavedFragmentState);
}
@@ -460,6 +465,9 @@
// If set this fragment is being retained across the current config change.
boolean mRetaining;
+ // If set this fragment's loaders are being retained across the current config change.
+ boolean mRetainLoader;
+
// If set this fragment has menu items to contribute.
boolean mHasMenu;
@@ -1166,6 +1174,12 @@
* android.content.Context#checkSelfPermission(String)}.
* </p>
* <p>
+ * Calling this API for permissions already granted to your app would show UI
+ * to the user to decide whether the app can still hold these permissions. This
+ * can be useful if the way your app uses data guarded by the permissions
+ * changes significantly.
+ * </p>
+ * <p>
* You cannot request a permission if your activity sets {@link
* android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
* <code>true</code> because in this case the activity would not receive
@@ -2401,7 +2415,7 @@
mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
- if (mRetaining) {
+ if (mRetainLoader) {
mLoaderManager.doRetain();
} else {
mLoaderManager.doStop();
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index 28dadfa..1b45137 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -341,6 +341,7 @@
*/
public void doLoaderStop(boolean retain) {
mHost.doLoaderStop(retain);
+ mHost.mFragmentManager.setRetainLoader(retain);
}
/**
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 132ffef..51d6132 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -869,6 +869,17 @@
}
}
+ void setRetainLoader(boolean retain) {
+ if (mActive != null) {
+ for (int i=0; i<mActive.size(); i++) {
+ Fragment f = mActive.get(i);
+ if (f != null) {
+ f.mRetainLoader = retain;
+ }
+ }
+ }
+ }
+
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
if (DEBUG && false) Log.v(TAG, "moveToState: " + f
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 47abf26..478fdd1 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -140,6 +140,8 @@
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
+ throws RemoteException;
public void resizeStack(int stackId, Rect bounds) throws RemoteException;
public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
public List<StackInfo> getAllStackInfos() throws RemoteException;
@@ -147,7 +149,6 @@
public boolean isInHomeStack(int taskId) throws RemoteException;
public void setFocusedStack(int stackId) throws RemoteException;
public int getFocusedStackId() throws RemoteException;
- public void activateActivity(IBinder token) throws RemoteException;
public void setFocusedTask(int taskId) throws RemoteException;
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException;
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
@@ -490,9 +491,7 @@
public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values)
throws RemoteException;
public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException;
- public void resizeTask(int taskId, Rect bounds) throws RemoteException;
- public void setActivityBounds(IBinder token, Rect bounds) throws RemoteException;
- public Rect getActivityBounds(IBinder token) throws RemoteException;
+ public void resizeTask(int taskId, Rect bounds, int resizeMode) throws RemoteException;
public Rect getTaskBounds(int taskId) throws RemoteException;
public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
@@ -536,6 +535,8 @@
public int getActivityStackId(IBinder token) throws RemoteException;
public void moveActivityToStack(IBinder token, int stackId) throws RemoteException;
+ public void suppressResizeConfigChanges(boolean suppress) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -891,7 +892,6 @@
int GET_ACTIVITY_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 343;
int MOVE_ACTIVITY_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 344;
int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
- int GET_ACTIVITY_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
- int SET_ACTIVITY_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
- int ACTIVATE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 348;
+ int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
+ int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index dc8f53d..2d78e19 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -65,7 +65,8 @@
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException;
void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
- Configuration config, Configuration overrideConfig) throws RemoteException;
+ Configuration config, Configuration overrideConfig, boolean preserveWindow)
+ throws RemoteException;
void scheduleNewIntent(List<ReferrerIntent> intent, IBinder token) throws RemoteException;
void scheduleDestroyActivity(IBinder token, boolean finished,
int configChanges) throws RemoteException;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index dee8d21..7184337 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1044,7 +1044,7 @@
activity.attach(context, aThread, this, token, 0, application, intent,
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
- new Configuration(), null, null);
+ new Configuration(), null, null, null);
return activity;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9381327..55aec59 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -953,6 +953,9 @@
private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
RemoteInput[] remoteInputs) {
this.mIcon = icon;
+ if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
+ this.icon = icon.getResId();
+ }
this.title = title;
this.actionIntent = intent;
this.mExtras = extras != null ? extras : new Bundle();
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 31d1ab7..5e8ad68 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -90,6 +90,9 @@
public static final int WINDOW_STATE_HIDING = 1;
public static final int WINDOW_STATE_HIDDEN = 2;
+ public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0;
+ public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1;
+
private Context mContext;
private IStatusBarService mService;
private IBinder mToken = new Binder();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 3d264c6..288a2cb 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -240,7 +240,7 @@
new CachedServiceFetcher<DevicePolicyManager>() {
@Override
public DevicePolicyManager createService(ContextImpl ctx) {
- return DevicePolicyManager.create(ctx, ctx.mMainThread.getHandler());
+ return DevicePolicyManager.create(ctx);
}});
registerService(Context.DOWNLOAD_SERVICE, DownloadManager.class,
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index d1e40ae..4e9adf0 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -20,6 +20,7 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -360,6 +361,7 @@
/**
* Return the component of the receiver that implements this device admin.
*/
+ @NonNull
public ComponentName getComponent() {
return new ComponentName(mReceiver.activityInfo.packageName,
mReceiver.activityInfo.name);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ac50699..e6484e9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -32,7 +32,6 @@
import android.graphics.Bitmap;
import android.net.ProxyInfo;
import android.os.Bundle;
-import android.os.Handler;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteCallback;
@@ -45,6 +44,7 @@
import android.service.restrictions.RestrictionsReceiver;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.org.conscrypt.TrustedCertificateStore;
import org.xmlpull.v1.XmlPullParserException;
@@ -87,18 +87,30 @@
private final Context mContext;
private final IDevicePolicyManager mService;
- private DevicePolicyManager(Context context, Handler handler) {
- mContext = context;
- mService = IDevicePolicyManager.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ private DevicePolicyManager(Context context) {
+ this(context, IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)));
}
/** @hide */
- public static DevicePolicyManager create(Context context, Handler handler) {
- DevicePolicyManager me = new DevicePolicyManager(context, handler);
+ @VisibleForTesting
+ protected DevicePolicyManager(Context context, IDevicePolicyManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /** @hide */
+ public static DevicePolicyManager create(Context context) {
+ DevicePolicyManager me = new DevicePolicyManager(context);
return me.mService != null ? me : null;
}
+ /** @hide test will override it. */
+ @VisibleForTesting
+ protected int myUserId() {
+ return UserHandle.myUserId();
+ }
+
/**
* Activity action: Starts the provisioning flow which sets up a managed profile.
*
@@ -823,7 +835,7 @@
* active (enabled) in the system.
*/
public boolean isAdminActive(@NonNull ComponentName admin) {
- return isAdminActiveAsUser(admin, UserHandle.myUserId());
+ return isAdminActiveAsUser(admin, myUserId());
}
/**
@@ -863,7 +875,7 @@
* returned.
*/
public List<ComponentName> getActiveAdmins() {
- return getActiveAdminsAsUser(UserHandle.myUserId());
+ return getActiveAdminsAsUser(myUserId());
}
/**
@@ -889,7 +901,7 @@
public boolean packageHasActiveAdmins(String packageName) {
if (mService != null) {
try {
- return mService.packageHasActiveAdmins(packageName, UserHandle.myUserId());
+ return mService.packageHasActiveAdmins(packageName, myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -906,7 +918,7 @@
public void removeActiveAdmin(@NonNull ComponentName admin) {
if (mService != null) {
try {
- mService.removeActiveAdmin(admin, UserHandle.myUserId());
+ mService.removeActiveAdmin(admin, myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -925,7 +937,7 @@
public boolean hasGrantedPolicy(@NonNull ComponentName admin, int usesPolicy) {
if (mService != null) {
try {
- return mService.hasGrantedPolicy(admin, usesPolicy, UserHandle.myUserId());
+ return mService.hasGrantedPolicy(admin, usesPolicy, myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1040,7 +1052,7 @@
* all admins.
*/
public int getPasswordQuality(@Nullable ComponentName admin) {
- return getPasswordQuality(admin, UserHandle.myUserId());
+ return getPasswordQuality(admin, myUserId());
}
/** @hide per-user version */
@@ -1093,7 +1105,7 @@
* all admins.
*/
public int getPasswordMinimumLength(@Nullable ComponentName admin) {
- return getPasswordMinimumLength(admin, UserHandle.myUserId());
+ return getPasswordMinimumLength(admin, myUserId());
}
/** @hide per-user version */
@@ -1154,7 +1166,7 @@
* password.
*/
public int getPasswordMinimumUpperCase(@Nullable ComponentName admin) {
- return getPasswordMinimumUpperCase(admin, UserHandle.myUserId());
+ return getPasswordMinimumUpperCase(admin, myUserId());
}
/** @hide per-user version */
@@ -1215,7 +1227,7 @@
* password.
*/
public int getPasswordMinimumLowerCase(@Nullable ComponentName admin) {
- return getPasswordMinimumLowerCase(admin, UserHandle.myUserId());
+ return getPasswordMinimumLowerCase(admin, myUserId());
}
/** @hide per-user version */
@@ -1273,7 +1285,7 @@
* @return The minimum number of letters required in the password.
*/
public int getPasswordMinimumLetters(@Nullable ComponentName admin) {
- return getPasswordMinimumLetters(admin, UserHandle.myUserId());
+ return getPasswordMinimumLetters(admin, myUserId());
}
/** @hide per-user version */
@@ -1332,7 +1344,7 @@
* @return The minimum number of numerical digits required in the password.
*/
public int getPasswordMinimumNumeric(@Nullable ComponentName admin) {
- return getPasswordMinimumNumeric(admin, UserHandle.myUserId());
+ return getPasswordMinimumNumeric(admin, myUserId());
}
/** @hide per-user version */
@@ -1390,7 +1402,7 @@
* @return The minimum number of symbols required in the password.
*/
public int getPasswordMinimumSymbols(@Nullable ComponentName admin) {
- return getPasswordMinimumSymbols(admin, UserHandle.myUserId());
+ return getPasswordMinimumSymbols(admin, myUserId());
}
/** @hide per-user version */
@@ -1449,7 +1461,7 @@
* @return The minimum number of letters required in the password.
*/
public int getPasswordMinimumNonLetter(@Nullable ComponentName admin) {
- return getPasswordMinimumNonLetter(admin, UserHandle.myUserId());
+ return getPasswordMinimumNonLetter(admin, myUserId());
}
/** @hide per-user version */
@@ -1540,7 +1552,7 @@
public long getPasswordExpirationTimeout(@Nullable ComponentName admin) {
if (mService != null) {
try {
- return mService.getPasswordExpirationTimeout(admin, UserHandle.myUserId());
+ return mService.getPasswordExpirationTimeout(admin, myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1561,7 +1573,7 @@
public long getPasswordExpiration(@Nullable ComponentName admin) {
if (mService != null) {
try {
- return mService.getPasswordExpiration(admin, UserHandle.myUserId());
+ return mService.getPasswordExpiration(admin, myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1577,7 +1589,7 @@
* @return The length of the password history
*/
public int getPasswordHistoryLength(@Nullable ComponentName admin) {
- return getPasswordHistoryLength(admin, UserHandle.myUserId());
+ return getPasswordHistoryLength(admin, myUserId());
}
/** @hide per-user version */
@@ -1617,7 +1629,7 @@
public boolean isActivePasswordSufficient() {
if (mService != null) {
try {
- return mService.isActivePasswordSufficient(UserHandle.myUserId());
+ return mService.isActivePasswordSufficient(myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1636,7 +1648,7 @@
public int getCurrentFailedPasswordAttempts() {
if (mService != null) {
try {
- return mService.getCurrentFailedPasswordAttempts(UserHandle.myUserId());
+ return mService.getCurrentFailedPasswordAttempts(myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1698,7 +1710,7 @@
* all admins.
*/
public int getMaximumFailedPasswordsForWipe(@Nullable ComponentName admin) {
- return getMaximumFailedPasswordsForWipe(admin, UserHandle.myUserId());
+ return getMaximumFailedPasswordsForWipe(admin, myUserId());
}
/** @hide per-user version */
@@ -1818,7 +1830,7 @@
* all admins if admin is null. Returns 0 if there are no restrictions.
*/
public long getMaximumTimeToLock(@Nullable ComponentName admin) {
- return getMaximumTimeToLock(admin, UserHandle.myUserId());
+ return getMaximumTimeToLock(admin, myUserId());
}
/** @hide per-user version */
@@ -1881,7 +1893,7 @@
public void wipeData(int flags) {
if (mService != null) {
try {
- mService.wipeData(flags, UserHandle.myUserId());
+ mService.wipeData(flags, myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1995,7 +2007,7 @@
public ComponentName getGlobalProxyAdmin() {
if (mService != null) {
try {
- return mService.getGlobalProxyAdmin(UserHandle.myUserId());
+ return mService.getGlobalProxyAdmin(myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2145,7 +2157,7 @@
public boolean getStorageEncryption(@Nullable ComponentName admin) {
if (mService != null) {
try {
- return mService.getStorageEncryption(admin, UserHandle.myUserId());
+ return mService.getStorageEncryption(admin, myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2173,7 +2185,7 @@
* or {@link #ENCRYPTION_STATUS_ACTIVE}.
*/
public int getStorageEncryptionStatus() {
- return getStorageEncryptionStatus(UserHandle.myUserId());
+ return getStorageEncryptionStatus(myUserId());
}
/** @hide per-user version */
@@ -2410,7 +2422,7 @@
* have disabled the camera
*/
public boolean getCameraDisabled(@Nullable ComponentName admin) {
- return getCameraDisabled(admin, UserHandle.myUserId());
+ return getCameraDisabled(admin, myUserId());
}
/** @hide per-user version */
@@ -2457,7 +2469,7 @@
* have disabled screen capture.
*/
public boolean getScreenCaptureDisabled(@Nullable ComponentName admin) {
- return getScreenCaptureDisabled(admin, UserHandle.myUserId());
+ return getScreenCaptureDisabled(admin, myUserId());
}
/** @hide per-user version */
@@ -2557,7 +2569,7 @@
* for a list.
*/
public int getKeyguardDisabledFeatures(@Nullable ComponentName admin) {
- return getKeyguardDisabledFeatures(admin, UserHandle.myUserId());
+ return getKeyguardDisabledFeatures(admin, myUserId());
}
/** @hide per-user version */
@@ -2590,7 +2602,7 @@
* @hide
*/
public void setActiveAdmin(@NonNull ComponentName policyReceiver, boolean refreshing) {
- setActiveAdmin(policyReceiver, refreshing, UserHandle.myUserId());
+ setActiveAdmin(policyReceiver, refreshing, myUserId());
}
/**
@@ -2627,7 +2639,7 @@
public void getRemoveWarning(@Nullable ComponentName admin, RemoteCallback result) {
if (mService != null) {
try {
- mService.getRemoveWarning(admin, result, UserHandle.myUserId());
+ mService.getRemoveWarning(admin, result, myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2956,7 +2968,7 @@
throws IllegalArgumentException {
if (mService != null) {
try {
- final int myUserId = UserHandle.myUserId();
+ final int myUserId = myUserId();
mService.setActiveAdmin(admin, false, myUserId);
return mService.setProfileOwner(admin, ownerName, myUserId);
} catch (RemoteException re) {
@@ -3301,7 +3313,7 @@
*/
public List<PersistableBundle> getTrustAgentConfiguration(@Nullable ComponentName admin,
@NonNull ComponentName agent) {
- return getTrustAgentConfiguration(admin, agent, UserHandle.myUserId());
+ return getTrustAgentConfiguration(admin, agent, myUserId());
}
/** @hide per-user version */
@@ -3927,7 +3939,7 @@
* @see #setAccountManagementDisabled
*/
public String[] getAccountTypesWithManagementDisabled() {
- return getAccountTypesWithManagementDisabledAsUser(UserHandle.myUserId());
+ return getAccountTypesWithManagementDisabledAsUser(myUserId());
}
/**
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 689283c..aa4a631 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -533,6 +533,14 @@
File file = scanQueue.remove(0);
String filePath;
try {
+ // Ignore symlinks outright
+ StructStat stat = Os.lstat(file.getPath());
+ if (OsConstants.S_ISLNK(stat.st_mode)) {
+ if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file);
+ continue;
+ }
+
+ // For all other verification, look at the canonicalized path
filePath = file.getCanonicalPath();
// prune this subtree?
@@ -544,11 +552,7 @@
}
// If it's a directory, enqueue its contents for scanning.
- StructStat stat = Os.lstat(filePath);
- if (OsConstants.S_ISLNK(stat.st_mode)) {
- if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file);
- continue;
- } else if (OsConstants.S_ISDIR(stat.st_mode)) {
+ if (OsConstants.S_ISDIR(stat.st_mode)) {
File[] contents = file.listFiles();
if (contents != null) {
for (File entry : contents) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e659049..670ca80 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1613,6 +1613,23 @@
= "android.intent.action.GET_PERMISSIONS_COUNT";
/**
+ * Broadcast action that requests list of all apps that have runtime permissions. It will
+ * respond to the request by sending a broadcast with action defined by
+ * {@link #EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT}. The response will contain
+ * {@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT}, as well as
+ * {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT}, with contents described below or
+ * a null upon failure.
+ *
+ * <p>{@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT} will contain a list of package names of
+ * apps that have runtime permissions. {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT}
+ * will contain the list of app labels corresponding ot the apps in the first list.
+ *
+ * @hide
+ */
+ public static final String ACTION_GET_PERMISSIONS_PACKAGES
+ = "android.intent.action.GET_PERMISSIONS_PACKAGES";
+
+ /**
* Extra included in response to {@link #ACTION_GET_PERMISSIONS_COUNT}.
* @hide
*/
@@ -1627,6 +1644,20 @@
= "android.intent.extra.GET_PERMISSIONS_GROUP_LIST_RESULT";
/**
+ * String list of apps that have one or more runtime permissions.
+ * @hide
+ */
+ public static final String EXTRA_GET_PERMISSIONS_APP_LIST_RESULT
+ = "android.intent.extra.GET_PERMISSIONS_APP_LIST_RESULT";
+
+ /**
+ * String list of app labels for apps that have one or more runtime permissions.
+ * @hide
+ */
+ public static final String EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT
+ = "android.intent.extra.GET_PERMISSIONS_APP_LABEL_LIST_RESULT";
+
+ /**
* Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_COUNT} broadcasts.
* @hide
*/
@@ -1634,6 +1665,13 @@
= "android.intent.extra.GET_PERMISSIONS_RESONSE_INTENT";
/**
+ * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_PACKAGES} broadcasts.
+ * @hide
+ */
+ public static final String EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT
+ = "android.intent.extra.GET_PERMISSIONS_PACKAGES_RESONSE_INTENT";
+
+ /**
* Activity action: Launch UI to manage which apps have a given permission.
* <p>
* Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission access
@@ -2995,6 +3033,39 @@
public static final String EXTRA_PROCESS_TEXT_READONLY =
"android.intent.extra.PROCESS_TEXT_READONLY";
+ /**
+ * Broadcast action: reports when a new thermal event has been reached. When the device
+ * is reaching its maximum temperatue, the thermal level reported
+ * {@hide}
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_THERMAL_EVENT = "android.intent.action.THERMAL_EVENT";
+
+ /** {@hide} */
+ public static final String EXTRA_THERMAL_STATE = "android.intent.extra.THERMAL_STATE";
+
+ /**
+ * Thermal state when the device is normal. This state is sent in the
+ * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@hide}
+ */
+ public static final int EXTRA_THERMAL_STATE_NORMAL = 0;
+
+ /**
+ * Thermal state where the device is approaching its maximum threshold. This state is sent in
+ * the {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@hide}
+ */
+ public static final int EXTRA_THERMAL_STATE_WARNING = 1;
+
+ /**
+ * Thermal state where the device has reached its maximum threshold. This state is sent in the
+ * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@hide}
+ */
+ public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2;
+
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index a59f429..a121b4d 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -33,12 +33,16 @@
*/
public class ActivityInfo extends ComponentInfo
implements Parcelable {
+
+ // NOTE: When adding new data members be sure to update the copy-constructor, Parcel
+ // constructor, and writeToParcel.
+
/**
* A style resource identifier (in the package's resources) of this
* activity's theme. From the "theme" attribute or, if not set, 0.
*/
public int theme;
-
+
/**
* Constant corresponding to <code>standard</code> in
* the {@link android.R.attr#launchMode} attribute.
@@ -707,6 +711,7 @@
super(orig);
theme = orig.theme;
launchMode = orig.launchMode;
+ documentLaunchMode = orig.documentLaunchMode;
permission = orig.permission;
taskAffinity = orig.taskAffinity;
targetActivity = orig.targetActivity;
@@ -788,6 +793,7 @@
super.writeToParcel(dest, parcelableFlags);
dest.writeInt(theme);
dest.writeInt(launchMode);
+ dest.writeInt(documentLaunchMode);
dest.writeString(permission);
dest.writeString(taskAffinity);
dest.writeString(targetActivity);
@@ -827,6 +833,7 @@
super(source);
theme = source.readInt();
launchMode = source.readInt();
+ documentLaunchMode = source.readInt();
permission = source.readString();
taskAffinity = source.readString();
targetActivity = source.readString();
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index cc06b67..f27fc2a 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -18,6 +18,7 @@
import android.graphics.drawable.Drawable;
import android.os.Parcel;
+import android.os.Parcelable;
import android.util.Printer;
/**
@@ -160,7 +161,12 @@
public void writeToParcel(Parcel dest, int parcelableFlags) {
super.writeToParcel(dest, parcelableFlags);
- applicationInfo.writeToParcel(dest, parcelableFlags);
+ if ((parcelableFlags & Parcelable.PARCELABLE_ELIDE_DUPLICATES) != 0) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ applicationInfo.writeToParcel(dest, parcelableFlags);
+ }
dest.writeString(processName);
dest.writeInt(descriptionRes);
dest.writeInt(enabled ? 1 : 0);
@@ -169,7 +175,10 @@
protected ComponentInfo(Parcel source) {
super(source);
- applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
+ final boolean hasApplicationInfo = (source.readInt() != 0);
+ if (hasApplicationInfo) {
+ applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
+ }
processName = source.readString();
descriptionRes = source.readInt();
enabled = (source.readInt() != 0);
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 9e6c6b5..d40bab5 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -305,10 +305,10 @@
dest.writeLong(firstInstallTime);
dest.writeLong(lastUpdateTime);
dest.writeIntArray(gids);
- dest.writeTypedArray(activities, parcelableFlags);
- dest.writeTypedArray(receivers, parcelableFlags);
- dest.writeTypedArray(services, parcelableFlags);
- dest.writeTypedArray(providers, parcelableFlags);
+ dest.writeTypedArray(activities, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+ dest.writeTypedArray(receivers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+ dest.writeTypedArray(services, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+ dest.writeTypedArray(providers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
dest.writeTypedArray(instrumentation, parcelableFlags);
dest.writeTypedArray(permissions, parcelableFlags);
dest.writeStringArray(requestedPermissions);
@@ -372,5 +372,22 @@
restrictedAccountType = source.readString();
requiredAccountType = source.readString();
overlayTarget = source.readString();
+
+ // The component lists were flattened with the redundant ApplicationInfo
+ // instances omitted. Distribute the canonical one here as appropriate.
+ if (applicationInfo != null) {
+ propagateApplicationInfo(applicationInfo, activities);
+ propagateApplicationInfo(applicationInfo, receivers);
+ propagateApplicationInfo(applicationInfo, services);
+ propagateApplicationInfo(applicationInfo, providers);
+ }
+ }
+
+ private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
+ if (components != null) {
+ for (ComponentInfo ci : components) {
+ ci.applicationInfo = appInfo;
+ }
+ }
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c8e9402..697b946 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2010,7 +2010,7 @@
* {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
* {@link #GET_SIGNATURES}, {@link #GET_UNINSTALLED_PACKAGES} to
* modify the data returned.
- * @return Returns a PackageInfo object containing information about the
+ * @return A PackageInfo object containing information about the
* package. If flag GET_UNINSTALLED_PACKAGES is set and if the
* package is not found in the list of installed applications, the
* package information is retrieved from the list of uninstalled
@@ -2032,6 +2032,46 @@
throws NameNotFoundException;
/**
+ * @hide
+ * Retrieve overall information about an application package that is
+ * installed on the system.
+ * <p>
+ * Throws {@link NameNotFoundException} if a package with the given name can
+ * not be found on the system.
+ *
+ * @param packageName The full name (i.e. com.google.apps.contacts) of the
+ * desired package.
+ * @param flags Additional option flags. Use any combination of
+ * {@link #GET_ACTIVITIES}, {@link #GET_GIDS},
+ * {@link #GET_CONFIGURATIONS}, {@link #GET_INSTRUMENTATION},
+ * {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+ * {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+ * {@link #GET_SIGNATURES}, {@link #GET_UNINSTALLED_PACKAGES} to
+ * modify the data returned.
+ * @param userId The user id.
+ * @return A PackageInfo object containing information about the
+ * package. If flag GET_UNINSTALLED_PACKAGES is set and if the
+ * package is not found in the list of installed applications, the
+ * package information is retrieved from the list of uninstalled
+ * applications (which includes installed applications as well as
+ * applications with data directory i.e. applications which had been
+ * deleted with {@code DONT_DELETE_DATA} flag set).
+ * @see #GET_ACTIVITIES
+ * @see #GET_GIDS
+ * @see #GET_CONFIGURATIONS
+ * @see #GET_INSTRUMENTATION
+ * @see #GET_PERMISSIONS
+ * @see #GET_PROVIDERS
+ * @see #GET_RECEIVERS
+ * @see #GET_SERVICES
+ * @see #GET_SIGNATURES
+ * @see #GET_UNINSTALLED_PACKAGES
+ */
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ public abstract PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+ throws NameNotFoundException;
+
+ /**
* Map from the current package names in use on the device to whatever
* the current canonical name of that package is.
* @param names Array of current names to be mapped.
@@ -3689,6 +3729,31 @@
Uri packageURI, PackageInstallObserver observer,
int flags, String installerPackageName);
+
+ /**
+ * @hide
+ * Install a package. Since this may take a little while, the result will be
+ * posted back to the given observer. An installation will fail if the package named
+ * in the package file's manifest is already installed, or if there's no space
+ * available on the device.
+ * @param packageURI The location of the package file to install. This can be a 'file:' or a
+ * 'content:' URI.
+ * @param observer An observer callback to get notified when the package installation is
+ * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be
+ * called when that happens. This parameter must not be null.
+ * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
+ * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
+ * @param installerPackageName Optional package name of the application that is performing the
+ * installation. This identifies which market the package came from.
+ * @param userId The user id.
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+ public abstract void installPackageAsUser(
+ Uri packageURI, PackageInstallObserver observer, int flags,
+ String installerPackageName, int userId);
+
/**
* Similar to
* {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
@@ -3752,7 +3817,17 @@
* @hide
*/
// @SystemApi
- public abstract int installExistingPackage(String packageName)
+ public abstract int installExistingPackage(String packageName) throws NameNotFoundException;
+
+ /**
+ * If there is already an application with the given package name installed
+ * on the system for other users, also install it for the specified user.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+ public abstract int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException;
/**
@@ -3958,7 +4033,7 @@
* @param observer An observer callback to get notified when the package deletion is
* complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
* called when that happens. observer may be null to indicate that no callback is desired.
- * @param flags - possible values: {@link #DELETE_KEEP_DATA},
+ * @param flags Possible values: {@link #DELETE_KEEP_DATA},
* {@link #DELETE_ALL_USERS}.
*
* @hide
@@ -3968,6 +4043,27 @@
String packageName, IPackageDeleteObserver observer, int flags);
/**
+ * Attempts to delete a package. Since this may take a little while, the result will
+ * be posted back to the given observer. A deletion will fail if the named package cannot be
+ * found, or if the named package is a "system package".
+ * (TODO: include pointer to documentation on "system packages")
+ *
+ * @param packageName The name of the package to delete
+ * @param observer An observer callback to get notified when the package deletion is
+ * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
+ * called when that happens. observer may be null to indicate that no callback is desired.
+ * @param flags Possible values: {@link #DELETE_KEEP_DATA}, {@link #DELETE_ALL_USERS}.
+ * @param userId The user Id
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+ public abstract void deletePackageAsUser(
+ String packageName, IPackageDeleteObserver observer, int flags, int userId);
+
+ /**
* Retrieve the package name of the application that installed a package. This identifies
* which market the package came from.
*
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 968f9b2..04b1a3b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -114,6 +114,12 @@
/** File name in an APK for the Android manifest. */
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+ /**
+ * File name in an APK for bytecode. There may be additional bytecode files
+ * but this one is always required for an APK that has code.
+ */
+ private static final String BYTECODE_FILENAME = "classes.dex";
+
/** Path prefix for apps on expanded storage */
private static final String MNT_EXPAND = "/mnt/expand/";
@@ -615,6 +621,7 @@
public final static int PARSE_IS_PRIVILEGED = 1<<7;
public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
+ public final static int PARSE_ENFORCE_CODE = 1<<10;
private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
@@ -1066,8 +1073,11 @@
private static void collectCertificates(Package pkg, File apkFile, int flags)
throws PackageParserException {
+ final boolean requireCode = ((flags & PARSE_ENFORCE_CODE) != 0)
+ && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0);
final String apkPath = apkFile.getAbsolutePath();
+ boolean codeFound = false;
StrictJarFile jarFile = null;
try {
jarFile = new StrictJarFile(apkPath);
@@ -1089,13 +1099,23 @@
final ZipEntry entry = i.next();
if (entry.isDirectory()) continue;
- if (entry.getName().startsWith("META-INF/")) continue;
- if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue;
+
+ final String entryName = entry.getName();
+ if (entryName.startsWith("META-INF/")) continue;
+ if (entryName.equals(ANDROID_MANIFEST_FILENAME)) continue;
+ if (entryName.equals(BYTECODE_FILENAME)) {
+ codeFound = true;
+ }
toVerify.add(entry);
}
}
+ if (!codeFound && requireCode) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Package " + apkPath + " code is missing");
+ }
+
// Verify that entries are signed consistently with the first entry
// we encountered. Note that for splits, certificates may have
// already been populated during an earlier parse of a base APK.
@@ -4561,6 +4581,17 @@
return applicationInfo.isUpdatedSystemApp();
}
+ /**
+ * @hide
+ */
+ public boolean canHaveOatDir() {
+ // The following app types CANNOT have oat directory
+ // - non-updated system apps
+ // - forward-locked apps or apps installed in ASEC containers
+ return (!isSystemApp() || isUpdatedSystemApp())
+ && !isForwardLocked() && !applicationInfo.isExternalAsec();
+ }
+
public String toString() {
return "Package{"
+ Integer.toHexString(System.identityHashCode(this))
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 7392563..d7c2215 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -136,7 +136,16 @@
* the method always returns false.
*/
public boolean isSystemOnly() {
- return id == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser();
+ return isSystemOnly(id);
+ }
+
+ /**
+ * Returns true if the given user is a split system user.
+ * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
+ * the method always returns false.
+ */
+ public static boolean isSystemOnly(int userId) {
+ return userId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser();
}
/**
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 927c02f..477b62c 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -61,7 +61,7 @@
* resource qualifier. 0 if undefined.
*/
public int mcc;
-
+
/**
* IMSI MNC (Mobile Network Code), corresponding to
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
@@ -199,7 +199,7 @@
* @hide
*/
public static final int SCREENLAYOUT_COMPAT_NEEDED = 0x10000000;
-
+
/**
* Bit mask of overall layout of the screen. Currently there are two
* fields:
@@ -207,11 +207,11 @@
* of the screen. They may be one of
* {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
* {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.</p>
- *
+ *
* <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
* is wider/taller than normal. They may be one of
* {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.</p>
- *
+ *
* <p>The {@link #SCREENLAYOUT_LAYOUTDIR_MASK} defines whether the screen layout
* is either LTR or RTL. They may be one of
* {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.</p>
@@ -295,6 +295,62 @@
return curLayout;
}
+ /** @hide */
+ public static String configurationDiffToString(int diff) {
+ ArrayList<String> list = new ArrayList<>();
+ if ((diff & ActivityInfo.CONFIG_MCC) != 0) {
+ list.add("CONFIG_MCC");
+ }
+ if ((diff & ActivityInfo.CONFIG_MNC) != 0) {
+ list.add("CONFIG_MNC");
+ }
+ if ((diff & ActivityInfo.CONFIG_LOCALE) != 0) {
+ list.add("CONFIG_LOCALE");
+ }
+ if ((diff & ActivityInfo.CONFIG_TOUCHSCREEN) != 0) {
+ list.add("CONFIG_TOUCHSCREEN");
+ }
+ if ((diff & ActivityInfo.CONFIG_KEYBOARD) != 0) {
+ list.add("CONFIG_KEYBOARD");
+ }
+ if ((diff & ActivityInfo.CONFIG_KEYBOARD_HIDDEN) != 0) {
+ list.add("CONFIG_KEYBOARD_HIDDEN");
+ }
+ if ((diff & ActivityInfo.CONFIG_NAVIGATION) != 0) {
+ list.add("CONFIG_NAVIGATION");
+ }
+ if ((diff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+ list.add("CONFIG_ORIENTATION");
+ }
+ if ((diff & ActivityInfo.CONFIG_SCREEN_LAYOUT) != 0) {
+ list.add("CONFIG_SCREEN_LAYOUT");
+ }
+ if ((diff & ActivityInfo.CONFIG_UI_MODE) != 0) {
+ list.add("CONFIG_UI_MODE");
+ }
+ if ((diff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+ list.add("CONFIG_SCREEN_SIZE");
+ }
+ if ((diff & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
+ list.add("CONFIG_SMALLEST_SCREEN_SIZE");
+ }
+ if ((diff & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
+ list.add("CONFIG_LAYOUT_DIRECTION");
+ }
+ if ((diff & ActivityInfo.CONFIG_FONT_SCALE) != 0) {
+ list.add("CONFIG_FONT_SCALE");
+ }
+ StringBuilder builder = new StringBuilder("{");
+ for (int i = 0, n = list.size(); i < n; i++) {
+ builder.append(list.get(i));
+ if (i != n - 1) {
+ builder.append(", ");
+ }
+ }
+ builder.append("}");
+ return builder.toString();
+ }
+
/**
* Check if the Configuration's current {@link #screenLayout} is at
* least the given size.
@@ -323,7 +379,7 @@
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
* resource qualifier. */
public static final int TOUCHSCREEN_FINGER = 3;
-
+
/**
* The kind of touch screen attached to the device.
* One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_FINGER}.
@@ -344,7 +400,7 @@
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
* resource qualifier. */
public static final int KEYBOARD_12KEY = 3;
-
+
/**
* The kind of keyboard attached to the device.
* One of: {@link #KEYBOARD_NOKEYS}, {@link #KEYBOARD_QWERTY},
@@ -364,7 +420,7 @@
public static final int KEYBOARDHIDDEN_YES = 2;
/** Constant matching actual resource implementation. {@hide} */
public static final int KEYBOARDHIDDEN_SOFT = 3;
-
+
/**
* A flag indicating whether any keyboard is available. Unlike
* {@link #hardKeyboardHidden}, this also takes into account a soft
@@ -373,7 +429,7 @@
* {@link #KEYBOARDHIDDEN_NO}, {@link #KEYBOARDHIDDEN_YES}.
*/
public int keyboardHidden;
-
+
/** Constant for {@link #hardKeyboardHidden}: a value indicating that no value has been set. */
public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
/** Constant for {@link #hardKeyboardHidden}, value corresponding to the
@@ -382,7 +438,7 @@
/** Constant for {@link #hardKeyboardHidden}, value corresponding to the
* physical keyboard being hidden. */
public static final int HARDKEYBOARDHIDDEN_YES = 2;
-
+
/**
* A flag indicating whether the hard keyboard has been hidden. This will
* be set on a device with a mechanism to hide the keyboard from the
@@ -390,7 +446,7 @@
* {@link #HARDKEYBOARDHIDDEN_NO}, {@link #HARDKEYBOARDHIDDEN_YES}.
*/
public int hardKeyboardHidden;
-
+
/** Constant for {@link #navigation}: a value indicating that no value has been set. */
public static final int NAVIGATION_UNDEFINED = 0;
/** Constant for {@link #navigation}, value corresponding to the
@@ -409,14 +465,14 @@
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
* resource qualifier. */
public static final int NAVIGATION_WHEEL = 4;
-
+
/**
* The kind of navigation method available on the device.
* One of: {@link #NAVIGATION_NONAV}, {@link #NAVIGATION_DPAD},
* {@link #NAVIGATION_TRACKBALL}, {@link #NAVIGATION_WHEEL}.
*/
public int navigation;
-
+
/** Constant for {@link #navigationHidden}: a value indicating that no value has been set. */
public static final int NAVIGATIONHIDDEN_UNDEFINED = 0;
/** Constant for {@link #navigationHidden}, value corresponding to the
@@ -427,7 +483,7 @@
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
* resource qualifier. */
public static final int NAVIGATIONHIDDEN_YES = 2;
-
+
/**
* A flag indicating whether any 5-way or DPAD navigation available.
* This will be set on a device with a mechanism to hide the navigation
@@ -435,7 +491,7 @@
* {@link #NAVIGATIONHIDDEN_NO}, {@link #NAVIGATIONHIDDEN_YES}.
*/
public int navigationHidden;
-
+
/** Constant for {@link #orientation}: a value indicating that no value has been set. */
public static final int ORIENTATION_UNDEFINED = 0;
/** Constant for {@link #orientation}, value corresponding to the
@@ -448,7 +504,7 @@
public static final int ORIENTATION_LANDSCAPE = 2;
/** @deprecated Not currently supported or used. */
@Deprecated public static final int ORIENTATION_SQUARE = 3;
-
+
/**
* Overall orientation of the screen. May be one of
* {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}.
@@ -692,7 +748,7 @@
compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
seq = o.seq;
}
-
+
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("{");
@@ -861,7 +917,7 @@
@Deprecated public void makeDefault() {
setToDefaults();
}
-
+
/**
* Copy the fields from delta into this Configuration object, keeping
* track of which ones have changed. Any undefined fields in
@@ -1001,7 +1057,7 @@
if (delta.seq != 0) {
seq = delta.seq;
}
-
+
return changed;
}
@@ -1119,12 +1175,12 @@
/**
* Determine if a new resource needs to be loaded from the bit set of
* configuration changes returned by {@link #updateFrom(Configuration)}.
- *
+ *
* @param configChanges The mask of changes configurations as returned by
* {@link #updateFrom(Configuration)}.
* @param interestingChanges The configuration changes that the resource
* can handled, as given in {@link android.util.TypedValue#changingConfigurations}.
- *
+ *
* @return Return true if the resource needs to be loaded, else false.
*/
public static boolean needNewResources(int configChanges, int interestingChanges) {
@@ -1159,7 +1215,7 @@
}
return diff > 0;
}
-
+
/**
* Parcelable methods
*/
@@ -1236,7 +1292,7 @@
compatSmallestScreenWidthDp = source.readInt();
seq = source.readInt();
}
-
+
public static final Parcelable.Creator<Configuration> CREATOR
= new Parcelable.Creator<Configuration>() {
public Configuration createFromParcel(Parcel source) {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 21ba7bd..121a187 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -359,6 +359,14 @@
}
}
+ public void requestColorTransform(int displayId, int colorTransformId) {
+ try {
+ mDm.requestColorTransform(displayId, colorTransformId);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to request color transform.", ex);
+ }
+ }
+
public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
String name, int width, int height, int densityDpi, Surface surface, int flags,
VirtualDisplay.Callback callback, Handler handler) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 4486dd4..8a1abf1 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -59,6 +59,9 @@
// No permissions required.
WifiDisplayStatus getWifiDisplayStatus();
+ // Requires CONFIGURE_DISPLAY_COLOR_TRANSFORM
+ void requestColorTransform(int displayId, int colorTransformId);
+
// Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
// MediaProjection token for certain combinations of flags.
int createVirtualDisplay(in IVirtualDisplayCallback callback,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 7fef5e1..04caa8f 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -772,10 +772,10 @@
if (mRemovalCallback != null) {
int reqFingerId = mRemovalFingerprint.getFingerId();
int reqGroupId = mRemovalFingerprint.getGroupId();
- if (fingerId != reqFingerId) {
+ if (reqFingerId != 0 && fingerId != reqFingerId) {
Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
}
- if (fingerId != reqFingerId) {
+ if (groupId != reqGroupId) {
Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
}
mRemovalCallback.onRemovalSucceeded(mRemovalFingerprint);
@@ -962,4 +962,3 @@
};
}
-
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 112ae10..8ab8991 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -436,9 +436,12 @@
try {
showWindow(true);
} catch (BadTokenException e) {
- if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
- mWindowVisible = false;
- mWindowAdded = false;
+ // We have ignored BadTokenException here since Jelly Bean MR-2 (API Level 18).
+ // We could ignore BadTokenException in InputMethodService#showWindow() instead,
+ // but it may break assumptions for those who override #showWindow() that we can
+ // detect errors in #showWindow() by checking BadTokenException.
+ // TODO: Investigate its feasibility. Update JavaDoc of #showWindow() of
+ // whether it's OK to override #showWindow() or not.
}
}
clearInsetOfPreviousIme();
@@ -1445,7 +1448,19 @@
mWindowWasVisible = mWindowVisible;
mInShowWindow = true;
showWindowInner(showInput);
+ } catch (BadTokenException e) {
+ // BadTokenException is a normal consequence in certain situations, e.g., swapping IMEs
+ // while there is a DO_SHOW_SOFT_INPUT message in the IIMethodWrapper queue.
+ if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
+ mWindowVisible = false;
+ mWindowAdded = false;
+ // Rethrow the exception to preserve the existing behavior. Some IMEs may have directly
+ // called this method and relied on this exception for some clean-up tasks.
+ // TODO: Give developers a clear guideline of whether it's OK to call this method or
+ // InputMethodManager#showSoftInputFromInputMethod() should always be used instead.
+ throw e;
} finally {
+ // TODO: Is it OK to set true when we get BadTokenException?
mWindowWasVisible = true;
mInShowWindow = false;
}
@@ -1456,15 +1471,9 @@
final int previousImeWindowStatus =
(mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
mWindowVisible = true;
- if (!mShowInputRequested) {
- if (mInputStarted) {
- if (showInput) {
- doShowInput = true;
- mShowInputRequested = true;
- }
- }
- } else {
- showInput = true;
+ if (!mShowInputRequested && mInputStarted && showInput) {
+ doShowInput = true;
+ mShowInputRequested = true;
}
if (DEBUG) Log.v(TAG, "showWindow: updating UI");
@@ -1985,31 +1994,25 @@
// We want our own movement method to handle the key, so the
// cursor will properly move in our own word wrapping.
if (count == MOVEMENT_DOWN) {
- if (movement.onKeyDown(eet,
- (Spannable)eet.getText(), keyCode, event)) {
+ if (movement.onKeyDown(eet, eet.getText(), keyCode, event)) {
reportExtractedMovement(keyCode, 1);
return true;
}
} else if (count == MOVEMENT_UP) {
- if (movement.onKeyUp(eet,
- (Spannable)eet.getText(), keyCode, event)) {
+ if (movement.onKeyUp(eet, eet.getText(), keyCode, event)) {
return true;
}
} else {
- if (movement.onKeyOther(eet, (Spannable)eet.getText(), event)) {
+ if (movement.onKeyOther(eet, eet.getText(), event)) {
reportExtractedMovement(keyCode, count);
} else {
KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
- if (movement.onKeyDown(eet,
- (Spannable)eet.getText(), keyCode, down)) {
+ if (movement.onKeyDown(eet, eet.getText(), keyCode, down)) {
KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
- movement.onKeyUp(eet,
- (Spannable)eet.getText(), keyCode, up);
+ movement.onKeyUp(eet, eet.getText(), keyCode, up);
while (--count > 0) {
- movement.onKeyDown(eet,
- (Spannable)eet.getText(), keyCode, down);
- movement.onKeyUp(eet,
- (Spannable)eet.getText(), keyCode, up);
+ movement.onKeyDown(eet, eet.getText(), keyCode, down);
+ movement.onKeyUp(eet, eet.getText(), keyCode, up);
}
reportExtractedMovement(keyCode, count);
}
@@ -2125,7 +2128,7 @@
} else {
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
- ic.commitText(String.valueOf((char) charCode), 1);
+ ic.commitText(String.valueOf(charCode), 1);
}
}
break;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9a2a241..444548f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -208,6 +208,12 @@
* {@link android.content.Intent#getParcelableExtra(String)}.
*/
public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+
+ /**
+ * Key for passing a URL to the captive portal login activity.
+ */
+ public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
+
/**
* Broadcast action to indicate the change of data activity status
* (idle or active) on a network in a recent period.
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index a939cce..3f36d65 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -245,7 +245,8 @@
intent.putExtra(EXTRA_NETWORKS_TO_SCORE, networks);
// A scorer should never become active if its package doesn't hold SCORE_NETWORKS, but
// ensure the package still holds it to be extra safe.
- mContext.sendBroadcastAsUser(intent, UserHandle.OWNER, Manifest.permission.SCORE_NETWORKS);
+ // TODO: http://b/23422763
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, Manifest.permission.SCORE_NETWORKS);
return true;
}
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index 4407c9d..78a9401 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.nfc.cardemulation;
import java.io.IOException;
diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java
index ad34e61..a299479 100644
--- a/core/java/android/nfc/cardemulation/HostApduService.java
+++ b/core/java/android/nfc/cardemulation/HostApduService.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.nfc.cardemulation;
import android.annotation.SdkConstant;
diff --git a/core/java/android/nfc/cardemulation/OffHostApduService.java b/core/java/android/nfc/cardemulation/OffHostApduService.java
index 0d01762..6a8aeee 100644
--- a/core/java/android/nfc/cardemulation/OffHostApduService.java
+++ b/core/java/android/nfc/cardemulation/OffHostApduService.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.nfc.cardemulation;
import android.annotation.SdkConstant;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index bad94fc..8e86a53 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -463,13 +463,15 @@
public abstract long getCpuPowerMaUs(int which);
/**
- * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed.
+ * 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.
* @param step the index of the CPU speed. This is not the actual speed of the CPU.
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
- * @see BatteryStats#getCpuSpeedSteps()
+ * @see PowerProfile.getNumCpuClusters()
+ * @see PowerProfile.getNumSpeedStepsInCpuCluster(int)
*/
- @Deprecated
- public abstract long getTimeAtCpuSpeed(int step, int which);
+ public abstract long getTimeAtCpuSpeed(int cluster, int step, int which);
public static abstract class Sensor {
/*
@@ -2276,9 +2278,6 @@
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
- /** Returns the number of different speeds that the CPU can run at */
- public abstract int getCpuSpeedSteps();
-
public abstract void writeToParcelWithoutUids(Parcel out, int flags);
private final static void formatTimeRaw(StringBuilder out, long seconds) {
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index cfa6164..c4501ba 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -157,7 +157,7 @@
* incoming transaction, then its own UserHandle is returned.
*/
public static final UserHandle getCallingUserHandle() {
- return new UserHandle(UserHandle.getUserId(getCallingUid()));
+ return UserHandle.of(UserHandle.getUserId(getCallingUid()));
}
/**
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index aeb5d45..4c19ddd 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -35,6 +35,7 @@
UserInfo createUser(in String name, int flags);
UserInfo createProfileForUser(in String name, int flags, int userHandle);
+ UserInfo createRestrictedProfile(String name, int parentUserId);
void setUserEnabled(int userHandle);
boolean removeUser(int userHandle);
void setUserName(int userHandle, String name);
@@ -48,6 +49,7 @@
UserInfo getUserInfo(int userHandle);
long getUserCreationTime(int userHandle);
boolean isRestricted();
+ boolean canHaveRestrictedProfile(int userId);
int getUserSerialNumber(int userHandle);
int getUserHandle(int userSerialNumber);
Bundle getUserRestrictions(int userHandle);
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 448b591..13b5de9 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -62,7 +62,17 @@
* may want to release resources at this point.
*/
public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
-
+
+ /**
+ * Flag for use with {@link #writeToParcel}: a parent object will take
+ * care of managing duplicate state/data that is nominally replicated
+ * across its inner data members. This flag instructs the inner data
+ * types to omit that data during marshaling. Exact behavior may vary
+ * on a case by case basis.
+ * @hide
+ */
+ public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002;
+
/**
* Bit masks for use with {@link #describeContents}: each bit represents a
* kind of object considered to have potential special significance when
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 70cff00..b6d0fcb 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -53,6 +53,15 @@
*/
public static final int WAKEFULNESS_DOZING = 3;
+
+ /**
+ * Power hint: The user is interacting with the device. The corresponding data field must be
+ * the expected duration of the fling, or 0 if unknown.
+ *
+ * This must be kept in sync with the values in hardware/libhardware/include/hardware/power.h
+ */
+ public static final int POWER_HINT_INTERACTION = 2;
+
public static String wakefulnessToString(int wakefulness) {
switch (wakefulness) {
case WAKEFULNESS_ASLEEP:
@@ -148,4 +157,6 @@
public abstract void updateUidProcState(int uid, int procState);
public abstract void uidGone(int uid);
+
+ public abstract void powerHint(int hintId, int data);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 7234e98..4ac361d0 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -797,8 +797,8 @@
* {@link #myUid()} in that a particular user will have multiple
* distinct apps running under it each with their own uid.
*/
- public static final UserHandle myUserHandle() {
- return new UserHandle(UserHandle.getUserId(myUid()));
+ public static UserHandle myUserHandle() {
+ return UserHandle.of(UserHandle.getUserId(myUid()));
}
/**
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 8c544f4..41de579 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -410,7 +410,7 @@
Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
+ context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,
android.Manifest.permission.MASTER_CLEAR,
new BroadcastReceiver() {
@Override
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 87ce12c..8b2c74f 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1923,7 +1923,7 @@
if (LOG_V) Log.d(TAG, "strict mode violation stacks read from binder call. i=" + i);
ViolationInfo info = new ViolationInfo(p, !currentlyGathering);
if (info.crashInfo.stackTrace != null && info.crashInfo.stackTrace.length() > 30000) {
- String front = info.crashInfo.stackTrace.substring(256);
+ String front = info.crashInfo.stackTrace.substring(0, 256);
// 30000 characters is way too large for this to be any sane kind of
// strict mode collection of stacks. We've had a problem where we leave
// strict mode violations associated with the thread, and it keeps tacking
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 48ede4f..796addc 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -17,7 +17,6 @@
package android.os;
import android.annotation.SystemApi;
-import android.util.SparseArray;
import java.io.PrintWriter;
@@ -57,15 +56,15 @@
/**
* @hide A user id constant to indicate the "owner" user of the device
- * @deprecated Consider using either USER_SYSTEM constant or
- * UserInfo.isPrimary().
+ * @deprecated Consider using either {@link UserHandle#USER_SYSTEM} constant or
+ * check the target user's flag {@link android.content.pm.UserInfo#isAdmin}.
*/
public static final int USER_OWNER = 0;
/**
* @hide A user handle to indicate the primary/owner user of the device
- * @deprecated Consider using either SYSTEM constant or
- * UserInfo.isPrimary().
+ * @deprecated Consider using either {@link UserHandle#SYSTEM} constant or
+ * check the target user's flag {@link android.content.pm.UserInfo#isAdmin}.
*/
public static final UserHandle OWNER = new UserHandle(USER_OWNER);
@@ -83,14 +82,12 @@
final int mHandle;
- private static final SparseArray<UserHandle> userHandles = new SparseArray<UserHandle>();
-
/**
* Checks to see if the user id is the same for the two uids, i.e., they belong to the same
* user.
* @hide
*/
- public static final boolean isSameUser(int uid1, int uid2) {
+ public static boolean isSameUser(int uid1, int uid2) {
return getUserId(uid1) == getUserId(uid2);
}
@@ -102,12 +99,12 @@
* @return whether the appId is the same for both uids
* @hide
*/
- public static final boolean isSameApp(int uid1, int uid2) {
+ public static boolean isSameApp(int uid1, int uid2) {
return getAppId(uid1) == getAppId(uid2);
}
/** @hide */
- public static final boolean isIsolated(int uid) {
+ public static boolean isIsolated(int uid) {
if (uid > 0) {
final int appId = getAppId(uid);
return appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID;
@@ -130,7 +127,7 @@
* Returns the user id for a given uid.
* @hide
*/
- public static final int getUserId(int uid) {
+ public static int getUserId(int uid) {
if (MU_ENABLED) {
return uid / PER_USER_RANGE;
} else {
@@ -139,27 +136,20 @@
}
/** @hide */
- public static final int getCallingUserId() {
+ public static int getCallingUserId() {
return getUserId(Binder.getCallingUid());
}
/** @hide */
- public static final UserHandle getCallingUserHandle() {
- int userId = getUserId(Binder.getCallingUid());
- UserHandle userHandle = userHandles.get(userId);
- // Intentionally not synchronized to save time
- if (userHandle == null) {
- userHandle = new UserHandle(userId);
- userHandles.put(userId, userHandle);
- }
- return userHandle;
+ public static UserHandle of(int userId) {
+ return userId == USER_SYSTEM ? SYSTEM : new UserHandle(userId);
}
/**
* Returns the uid that is composed from the userId and the appId.
* @hide
*/
- public static final int getUid(int userId, int appId) {
+ public static int getUid(int userId, int appId) {
if (MU_ENABLED) {
return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
} else {
@@ -171,7 +161,7 @@
* Returns the app id (or base uid) for a given uid, stripping out the user id from it.
* @hide
*/
- public static final int getAppId(int uid) {
+ public static int getAppId(int uid) {
return uid % PER_USER_RANGE;
}
@@ -179,7 +169,7 @@
* Returns the gid shared between all apps with this userId.
* @hide
*/
- public static final int getUserGid(int userId) {
+ public static int getUserGid(int userId) {
return getUid(userId, Process.SHARED_USER_GID);
}
@@ -187,7 +177,7 @@
* Returns the shared app gid for a given uid or appId.
* @hide
*/
- public static final int getSharedAppGid(int id) {
+ public static int getSharedAppGid(int id) {
return Process.FIRST_SHARED_APPLICATION_GID + (id % PER_USER_RANGE)
- Process.FIRST_APPLICATION_UID;
}
@@ -196,7 +186,7 @@
* Returns the app id for a given shared app gid. Returns -1 if the ID is invalid.
* @hide
*/
- public static final int getAppIdFromSharedAppGid(int gid) {
+ public static int getAppIdFromSharedAppGid(int gid) {
final int appId = getAppId(gid) + Process.FIRST_APPLICATION_UID
- Process.FIRST_SHARED_APPLICATION_GID;
if (appId < 0 || appId >= Process.FIRST_SHARED_APPLICATION_GID) {
@@ -272,7 +262,7 @@
* @hide
*/
@SystemApi
- public static final int myUserId() {
+ public static int myUserId() {
return getUserId(Process.myUid());
}
@@ -280,9 +270,10 @@
* Returns true if this UserHandle refers to the owner user; false otherwise.
* @return true if this UserHandle refers to the owner user; false otherwise.
* @hide
+ * TODO: find an alternative to this Api.
*/
@SystemApi
- public final boolean isOwner() {
+ public boolean isOwner() {
return this.equals(OWNER);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 64e2505..37467ca 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -15,6 +15,7 @@
*/
package android.os;
+import android.accounts.AccountManager;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.ActivityManager;
@@ -631,6 +632,19 @@
}
/**
+ * Checks if specified user can have restricted profile.
+ * @hide
+ */
+ public boolean canHaveRestrictedProfile(int userId) {
+ try {
+ return mService.canHaveRestrictedProfile(userId);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not check if user can have restricted profile", re);
+ return false;
+ }
+ }
+
+ /**
* Checks if the calling app is running as a guest user.
* @return whether the caller is a guest user.
* @hide
@@ -927,7 +941,8 @@
}
/**
- * Creates a restricted profile with the specified name.
+ * Creates a restricted profile with the specified name. This method also sets necessary
+ * restrictions and adds shared accounts.
*
* @param name profile's name
* @return UserInfo object for the created user, or null if the user could not be created.
@@ -935,13 +950,14 @@
*/
public UserInfo createRestrictedProfile(String name) {
try {
- if (isSplitSystemUser()) {
- return mService.createProfileForUser(name, UserInfo.FLAG_RESTRICTED,
- UserHandle.getCallingUserId());
- } else {
- return mService.createProfileForUser(name, UserInfo.FLAG_RESTRICTED,
- UserHandle.USER_SYSTEM);
+ UserHandle parentUserHandle = Process.myUserHandle();
+ UserInfo user = mService.createRestrictedProfile(name,
+ parentUserHandle.getIdentifier());
+ if (user != null) {
+ AccountManager.get(mContext).addSharedAccountsFromParentUser(parentUserHandle,
+ UserHandle.of(user.id));
}
+ return user;
} catch (RemoteException e) {
Log.w(TAG, "Could not create a restricted profile", e);
}
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 6888594..c368e5a 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -438,6 +438,8 @@
final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(uri);
+ intent.putExtra(DocumentsContract.EXTRA_SHOW_FILESIZE, true);
+ intent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
return intent;
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 59609f9..1a83cd5 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -93,6 +93,9 @@
public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED";
/** {@hide} */
+ public static final String EXTRA_SHOW_FILESIZE = "android.content.extra.SHOW_FILESIZE";
+
+ /** {@hide} */
public static final String EXTRA_TARGET_URI = "android.content.extra.TARGET_URI";
/**
@@ -266,7 +269,7 @@
* writability of a document may change over time, for example due to
* remote access changes. This flag indicates that a document client can
* expect {@link ContentResolver#openOutputStream(Uri)} to succeed.
- *
+ *
* @see #COLUMN_FLAGS
*/
public static final int FLAG_SUPPORTS_WRITE = 1 << 1;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d1744b4..225f0cf 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3283,7 +3283,6 @@
DOCK_SOUNDS_ENABLED, // moved to global
LOCKSCREEN_SOUNDS_ENABLED,
SHOW_WEB_SUGGESTIONS,
- NOTIFICATION_LIGHT_PULSE,
SIP_CALL_OPTIONS,
SIP_RECEIVE_CALLS,
POINTER_SPEED,
@@ -4921,7 +4920,26 @@
"accessibility_display_daltonizer";
/**
- * The timout for considering a press to be a long press in milliseconds.
+ * Setting that specifies whether automatic click when the mouse pointer stops moving is
+ * enabled.
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_AUTOCLICK_ENABLED =
+ "accessibility_autoclick_enabled";
+
+ /**
+ * Integer setting specifying amount of time in ms the mouse pointer has to stay still
+ * before performing click when {@link #ACCESSIBILITY_AUTOCLICK_ENABLED} is set.
+ *
+ * @see #ACCESSIBILITY_AUTOCLICK_ENABLED
+ * @hide
+ */
+ public static final String ACCESSIBILITY_AUTOCLICK_DELAY =
+ "accessibility_autoclick_delay";
+
+ /**
+ * The timeout for considering a press to be a long press in milliseconds.
* @hide
*/
public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
@@ -5719,6 +5737,15 @@
public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
/**
+ * Whether the camera launch gesture to double tap the power button when the screen is off
+ * should be disabled.
+ *
+ * @hide
+ */
+ public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED =
+ "camera_double_tap_power_gesture_disabled";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -5776,6 +5803,8 @@
SLEEP_TIMEOUT,
DOUBLE_TAP_TO_WAKE,
CAMERA_GESTURE_DISABLED,
+ ACCESSIBILITY_AUTOCLICK_ENABLED,
+ ACCESSIBILITY_AUTOCLICK_DELAY
};
/**
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index 409542d..7cf1d71 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -31,7 +31,7 @@
*/
interface IKeystoreService {
int getState(int userId);
- byte[] get(String name);
+ byte[] get(String name, int uid);
int insert(String name, in byte[] item, int uid, int flags);
int del(String name, int uid);
int exist(String name, int uid);
@@ -49,7 +49,7 @@
byte[] get_pubkey(String name);
int grant(String name, int granteeUid);
int ungrant(String name, int granteeUid);
- long getmtime(String name);
+ long getmtime(String name, int uid);
int duplicate(String srcKey, int srcUid, String destKey, int destUid);
int is_hardware_backed(String string);
int clear_uid(long uid);
@@ -59,13 +59,13 @@
int generateKey(String alias, in KeymasterArguments arguments, in byte[] entropy, int uid,
int flags, out KeyCharacteristics characteristics);
int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId,
- out KeyCharacteristics characteristics);
+ int uid, out KeyCharacteristics characteristics);
int importKey(String alias, in KeymasterArguments arguments, int format,
in byte[] keyData, int uid, int flags, out KeyCharacteristics characteristics);
ExportResult exportKey(String alias, int format, in KeymasterBlob clientId,
- in KeymasterBlob appId);
+ in KeymasterBlob appId, int uid);
OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable,
- in KeymasterArguments params, in byte[] entropy);
+ in KeymasterArguments params, in byte[] entropy, int uid);
OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature,
in byte[] entropy);
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 18dc262..954dcfb 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -364,18 +364,32 @@
for (int k = 0; k < val.length; k += incr) {
switch (cmd) {
case 'm': // moveto - Start a new sub-path (relative)
- path.rMoveTo(val[k + 0], val[k + 1]);
currentX += val[k + 0];
currentY += val[k + 1];
- currentSegmentStartX = currentX;
- currentSegmentStartY = currentY;
+ if (k > 0) {
+ // According to the spec, if a moveto is followed by multiple
+ // pairs of coordinates, the subsequent pairs are treated as
+ // implicit lineto commands.
+ path.rLineTo(val[k + 0], val[k + 1]);
+ } else {
+ path.rMoveTo(val[k + 0], val[k + 1]);
+ currentSegmentStartX = currentX;
+ currentSegmentStartY = currentY;
+ }
break;
case 'M': // moveto - Start a new sub-path
- path.moveTo(val[k + 0], val[k + 1]);
currentX = val[k + 0];
currentY = val[k + 1];
- currentSegmentStartX = currentX;
- currentSegmentStartY = currentY;
+ if (k > 0) {
+ // According to the spec, if a moveto is followed by multiple
+ // pairs of coordinates, the subsequent pairs are treated as
+ // implicit lineto commands.
+ path.lineTo(val[k + 0], val[k + 1]);
+ } else {
+ path.moveTo(val[k + 0], val[k + 1]);
+ currentSegmentStartX = currentX;
+ currentSegmentStartY = currentY;
+ }
break;
case 'l': // lineto - Draw a line from the current point (relative)
path.rLineTo(val[k + 0], val[k + 1]);
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
new file mode 100644
index 0000000..b1d28e0
--- /dev/null
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.os.Looper;
+
+/**
+ * Similar to {@link InputEventReceiver}, but batches events to vsync boundaries when possible.
+ * @hide
+ */
+public class BatchedInputEventReceiver extends InputEventReceiver {
+ Choreographer mChoreographer;
+ private boolean mBatchedInputScheduled;
+
+ public BatchedInputEventReceiver(
+ InputChannel inputChannel, Looper looper, Choreographer choreographer) {
+ super(inputChannel, looper);
+ mChoreographer = choreographer;
+ }
+
+ @Override
+ public void onBatchedInputEventPending() {
+ scheduleBatchedInput();
+ }
+
+ @Override
+ public void dispose() {
+ unscheduleBatchedInput();
+ super.dispose();
+ }
+
+ void doConsumeBatchedInput(long frameTimeNanos) {
+ if (mBatchedInputScheduled) {
+ mBatchedInputScheduled = false;
+ if (consumeBatchedInputEvents(frameTimeNanos) && frameTimeNanos != -1) {
+ // If we consumed a batch here, we want to go ahead and schedule the
+ // consumption of batched input events on the next frame. Otherwise, we would
+ // wait until we have more input events pending and might get starved by other
+ // things occurring in the process. If the frame time is -1, however, then
+ // we're in a non-batching mode, so there's no need to schedule this.
+ scheduleBatchedInput();
+ }
+ }
+ }
+
+ private void scheduleBatchedInput() {
+ if (!mBatchedInputScheduled) {
+ mBatchedInputScheduled = true;
+ mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
+ }
+ }
+
+ private void unscheduleBatchedInput() {
+ if (mBatchedInputScheduled) {
+ mBatchedInputScheduled = false;
+ mChoreographer.removeCallbacks(
+ Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
+ }
+ }
+
+ private final class BatchedInputRunnable implements Runnable {
+ @Override
+ public void run() {
+ doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
+ }
+ }
+ private final BatchedInputRunnable mBatchedInputRunnable = new BatchedInputRunnable();
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 35c4192..1269ad9 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -16,7 +16,10 @@
package android.view;
+import android.annotation.RequiresPermission;
+import android.content.Context;
import android.content.res.CompatibilityInfo;
+import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -30,6 +33,8 @@
import java.util.Arrays;
+import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM;
+
/**
* Provides information about the size and density of a logical display.
* <p>
@@ -679,6 +684,49 @@
}
/**
+ * Request the display applies a color transform.
+ * @hide
+ */
+ @RequiresPermission(CONFIGURE_DISPLAY_COLOR_TRANSFORM)
+ public void requestColorTransform(ColorTransform colorTransform) {
+ mGlobal.requestColorTransform(mDisplayId, colorTransform.getId());
+ }
+
+ /**
+ * Returns the active color transform of this display
+ * @hide
+ */
+ public ColorTransform getColorTransform() {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.getColorTransform();
+ }
+ }
+
+ /**
+ * Returns the default color transform of this display
+ * @hide
+ */
+ public ColorTransform getDefaultColorTransform() {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.getDefaultColorTransform();
+ }
+ }
+
+ /**
+ * Gets the supported color transforms of this device.
+ * @hide
+ */
+ public ColorTransform[] getSupportedColorTransforms() {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ ColorTransform[] transforms = mDisplayInfo.supportedColorTransforms;
+ return Arrays.copyOf(transforms, transforms.length);
+ }
+ }
+
+ /**
* Gets the app VSYNC offset, in nanoseconds. This is a positive value indicating
* the phase offset of the VSYNC events provided by Choreographer relative to the
* display refresh. For example, if Choreographer reports that the refresh occurred
@@ -1054,4 +1102,89 @@
}
};
}
+
+ /**
+ * A color transform supported by a given display.
+ *
+ * @see Display#getSupportedColorTransforms()
+ * @hide
+ */
+ public static final class ColorTransform implements Parcelable {
+ public static final ColorTransform[] EMPTY_ARRAY = new ColorTransform[0];
+
+ private final int mId;
+ private final int mColorTransform;
+
+ public ColorTransform(int id, int colorTransform) {
+ mId = id;
+ mColorTransform = colorTransform;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public int getColorTransform() {
+ return mColorTransform;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof ColorTransform)) {
+ return false;
+ }
+ ColorTransform that = (ColorTransform) other;
+ return mId == that.mId
+ && mColorTransform == that.mColorTransform;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 1;
+ hash = hash * 17 + mId;
+ hash = hash * 17 + mColorTransform;
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("{")
+ .append("id=").append(mId)
+ .append(", colorTransform=").append(mColorTransform)
+ .append("}")
+ .toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private ColorTransform(Parcel in) {
+ this(in.readInt(), in.readInt());
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int parcelableFlags) {
+ out.writeInt(mId);
+ out.writeInt(mColorTransform);
+ }
+
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<ColorTransform> CREATOR
+ = new Parcelable.Creator<ColorTransform>() {
+ @Override
+ public ColorTransform createFromParcel(Parcel in) {
+ return new ColorTransform(in);
+ }
+
+ @Override
+ public ColorTransform[] newArray(int size) {
+ return new ColorTransform[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index cf17990..ee76274 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -169,6 +169,15 @@
*/
public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
+ /** The active color transform. */
+ public int colorTransformId;
+
+ /** The default color transform. */
+ public int defaultColorTransformId;
+
+ /** The list of supported color transforms */
+ public Display.ColorTransform[] supportedColorTransforms = Display.ColorTransform.EMPTY_ARRAY;
+
/**
* The logical display density which is the basis for density-independent
* pixels.
@@ -279,6 +288,8 @@
&& rotation == other.rotation
&& modeId == other.modeId
&& defaultModeId == other.defaultModeId
+ && colorTransformId == other.colorTransformId
+ && defaultColorTransformId == other.defaultColorTransformId
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
&& physicalYDpi == other.physicalYDpi
@@ -317,6 +328,10 @@
modeId = other.modeId;
defaultModeId = other.defaultModeId;
supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
+ colorTransformId = other.colorTransformId;
+ defaultColorTransformId = other.defaultColorTransformId;
+ supportedColorTransforms = Arrays.copyOf(
+ other.supportedColorTransforms, other.supportedColorTransforms.length);
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
physicalYDpi = other.physicalYDpi;
@@ -353,6 +368,13 @@
for (int i = 0; i < nModes; i++) {
supportedModes[i] = Display.Mode.CREATOR.createFromParcel(source);
}
+ colorTransformId = source.readInt();
+ defaultColorTransformId = source.readInt();
+ int nColorTransforms = source.readInt();
+ supportedColorTransforms = new Display.ColorTransform[nColorTransforms];
+ for (int i = 0; i < nColorTransforms; i++) {
+ supportedColorTransforms[i] = Display.ColorTransform.CREATOR.createFromParcel(source);
+ }
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
physicalYDpi = source.readFloat();
@@ -390,6 +412,12 @@
for (int i = 0; i < supportedModes.length; i++) {
supportedModes[i].writeToParcel(dest, flags);
}
+ dest.writeInt(colorTransformId);
+ dest.writeInt(defaultColorTransformId);
+ dest.writeInt(supportedColorTransforms.length);
+ for (int i = 0; i < supportedColorTransforms.length; i++) {
+ supportedColorTransforms[i].writeToParcel(dest, flags);
+ }
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
dest.writeFloat(physicalYDpi);
@@ -461,6 +489,24 @@
return result;
}
+ public Display.ColorTransform getColorTransform() {
+ return findColorTransform(colorTransformId);
+ }
+
+ public Display.ColorTransform getDefaultColorTransform() {
+ return findColorTransform(defaultColorTransformId);
+ }
+
+ private Display.ColorTransform findColorTransform(int colorTransformId) {
+ for (int i = 0; i < supportedColorTransforms.length; i++) {
+ Display.ColorTransform colorTransform = supportedColorTransforms[i];
+ if (colorTransform.getId() == colorTransformId) {
+ return colorTransform;
+ }
+ }
+ throw new IllegalStateException("Unable to locate color transform: " + colorTransformId);
+ }
+
public void getAppMetrics(DisplayMetrics outMetrics) {
getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
@@ -562,6 +608,12 @@
sb.append(defaultModeId);
sb.append(", modes ");
sb.append(Arrays.toString(supportedModes));
+ sb.append(", colorTransformId ");
+ sb.append(colorTransformId);
+ sb.append(", defaultColorTransformId ");
+ sb.append(defaultColorTransformId);
+ sb.append(", supportedColorTransforms ");
+ sb.append(Arrays.toString(supportedColorTransforms));
sb.append(", rotation ");
sb.append(rotation);
sb.append(", density ");
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index f559e21..34835f4 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -128,6 +128,8 @@
float mX, mY;
ClipDescription mClipDescription;
ClipData mClipData;
+ DropPermissionHolder mDropPermissionHolder;
+
Object mLocalState;
boolean mDragResult;
@@ -253,28 +255,30 @@
}
private void init(int action, float x, float y, ClipDescription description, ClipData data,
- Object localState, boolean result) {
+ DropPermissionHolder dropPermissionHolder, Object localState, boolean result) {
mAction = action;
mX = x;
mY = y;
mClipDescription = description;
mClipData = data;
+ mDropPermissionHolder = dropPermissionHolder;
mLocalState = localState;
mDragResult = result;
}
static DragEvent obtain() {
- return DragEvent.obtain(0, 0f, 0f, null, null, null, false);
+ return DragEvent.obtain(0, 0f, 0f, null, null, null, null, false);
}
/** @hide */
public static DragEvent obtain(int action, float x, float y, Object localState,
- ClipDescription description, ClipData data, boolean result) {
+ ClipDescription description, ClipData data, DropPermissionHolder dropPermissionHolder,
+ boolean result) {
final DragEvent ev;
synchronized (gRecyclerLock) {
if (gRecyclerTop == null) {
ev = new DragEvent();
- ev.init(action, x, y, description, data, localState, result);
+ ev.init(action, x, y, description, data, dropPermissionHolder, localState, result);
return ev;
}
ev = gRecyclerTop;
@@ -285,7 +289,7 @@
ev.mRecycled = false;
ev.mNext = null;
- ev.init(action, x, y, description, data, localState, result);
+ ev.init(action, x, y, description, data, dropPermissionHolder, localState, result);
return ev;
}
@@ -293,7 +297,8 @@
/** @hide */
public static DragEvent obtain(DragEvent source) {
return obtain(source.mAction, source.mX, source.mY, source.mLocalState,
- source.mClipDescription, source.mClipData, source.mDragResult);
+ source.mClipDescription, source.mClipData, source.mDropPermissionHolder,
+ source.mDragResult);
}
/**
@@ -358,6 +363,17 @@
}
/**
+ * Returns the {@link android.view.DropPermissionHolder} object that can be used by the drag
+ * listener to request and release the permissions for the content URIs contained in the
+ * {@link android.content.ClipData} object associated with this event.
+ * This method only returns valid data if the event action is {@link #ACTION_DROP}.
+ * @return The DropPermissionHolder object used to handle content URI permissions.
+ */
+ public DropPermissionHolder getDropPermissionHolder() {
+ return mDropPermissionHolder;
+ }
+
+ /**
* Returns the local state object sent to the system as part of the call to
* {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}.
* The object is intended to provide local information about the drag and drop operation. For
@@ -477,6 +493,12 @@
dest.writeInt(1);
mClipDescription.writeToParcel(dest, flags);
}
+ if (mDropPermissionHolder == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mDropPermissionHolder.writeToParcel(dest, flags);
+ }
}
/**
@@ -496,6 +518,9 @@
if (in.readInt() != 0) {
event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
}
+ if (in.readInt() != 0) {
+ event.mDropPermissionHolder = DropPermissionHolder.CREATOR.createFromParcel(in);
+ }
return event;
}
diff --git a/core/java/android/view/DropPermissionHolder.java b/core/java/android/view/DropPermissionHolder.java
new file mode 100644
index 0000000..993e67a
--- /dev/null
+++ b/core/java/android/view/DropPermissionHolder.java
@@ -0,0 +1,161 @@
+package android.view;
+
+import android.app.IActivityManager;
+import android.content.ClipData;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import com.android.internal.view.IDropPermissionHolder;
+
+import java.util.ArrayList;
+
+public class DropPermissionHolder implements Parcelable {
+
+ IDropPermissionHolder mDropPermissionHolder;
+
+ /**
+ * Create a new DropPermissionHolder to be passed to the client with a DragEvent.
+ *
+ * @hide
+ */
+ public DropPermissionHolder(ClipData clipData, IActivityManager activityManager,
+ int sourceUid, String targetPackage, int mode, int sourceUserId, int targetUserId) {
+ mDropPermissionHolder = new LocalDropPermissionHolder(clipData, activityManager,
+ sourceUid, targetPackage, mode, sourceUserId, targetUserId);
+ }
+
+ private class LocalDropPermissionHolder extends IDropPermissionHolder.Stub {
+
+ private final IActivityManager mActivityManager;
+ private final int mSourceUid;
+ private final String mTargetPackage;
+ private final int mMode;
+ private final int mSourceUserId;
+ private final int mTargetUserId;
+
+ IBinder mPermissionOwner = null;
+
+ final private ArrayList<Uri> mUris = new ArrayList<Uri>();
+
+ LocalDropPermissionHolder(ClipData clipData, IActivityManager activityManager,
+ int sourceUid, String targetPackage, int mode, int sourceUserId, int targetUserId) {
+ mActivityManager = activityManager;
+ mSourceUid = sourceUid;
+ mTargetPackage = targetPackage;
+ mMode = mode;
+ mSourceUserId = sourceUserId;
+ mTargetUserId = targetUserId;
+
+ int N = clipData.getItemCount();
+ for (int i = 0; i != N; ++i) {
+ ClipData.Item item = clipData.getItemAt(i);
+
+ if (item.getUri() != null) {
+ mUris.add(item.getUri());
+ }
+
+ Intent intent = item.getIntent();
+ if (intent != null && intent.getData() != null) {
+ mUris.add(intent.getData());
+ }
+ }
+ }
+
+ @Override
+ public void grant() throws RemoteException {
+ if (mPermissionOwner != null) {
+ return;
+ }
+
+ mPermissionOwner = mActivityManager.newUriPermissionOwner("drop");
+
+ long origId = Binder.clearCallingIdentity();
+ try {
+ for (Uri mUri : mUris) {
+ mActivityManager.grantUriPermissionFromOwner(
+ mPermissionOwner, mSourceUid, mTargetPackage, mUri, mMode,
+ mSourceUserId, mTargetUserId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ }
+
+ @Override
+ public void revoke() throws RemoteException {
+ if (mPermissionOwner == null) {
+ return;
+ }
+
+ for (Uri mUri : mUris) {
+ mActivityManager.revokeUriPermissionFromOwner(
+ mPermissionOwner, mUri, mMode, mSourceUserId);
+ }
+
+ mPermissionOwner = null;
+ }
+ }
+
+ /**
+ * Request permissions granted by the activity which started the drag.
+ */
+ public void grant() {
+ try {
+ mDropPermissionHolder.grant();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Revoke permissions granted by the {@link #grant()} call.
+ */
+ public void revoke() {
+ try {
+ mDropPermissionHolder.revoke();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Returns information about the {@link android.os.Parcel} representation of this
+ * DropPermissionHolder object.
+ * @return Information about the {@link android.os.Parcel} representation.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Creates a {@link android.os.Parcel} object from this DropPermissionHolder object.
+ * @param dest A {@link android.os.Parcel} object in which to put the DropPermissionHolder
+ * object.
+ * @param flags Flags to store in the Parcel.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mDropPermissionHolder.asBinder());
+ }
+
+ DropPermissionHolder(Parcel in) {
+ mDropPermissionHolder = IDropPermissionHolder.Stub.asInterface(in.readStrongBinder());
+ }
+
+ /**
+ * A container for creating a DropPermissionHolder from a Parcel.
+ */
+ public static final Parcelable.Creator<DropPermissionHolder> CREATOR
+ = new Parcelable.Creator<DropPermissionHolder>() {
+ public DropPermissionHolder createFromParcel(Parcel in) {
+ return new DropPermissionHolder(in);
+ }
+ public DropPermissionHolder[] newArray(int size) {
+ return new DropPermissionHolder[size];
+ }
+ };
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 33c51ff..7adfa8d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -98,11 +98,12 @@
* @param taskBounds Bounds to use when creating a new Task with the input task Id if
* the task doesn't exist yet.
* @param configuration Configuration that is being used with this task.
+ * @param cropWindowsToStack True if the app windows should be cropped to the stack bounds.
*/
void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
- in Rect taskBounds, in Configuration configuration);
+ in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack);
/**
*
* @param token The token we are adding to the input task Id.
@@ -143,7 +144,6 @@
void setAppStartingWindow(IBinder token, String pkg, int theme,
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
- void setAppWillBeHidden(IBinder token);
void setAppVisibility(IBinder token, boolean visible);
void startAppFreezingScreen(IBinder token, int configChanges);
void stopAppFreezingScreen(IBinder token, boolean force);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 73b4a6e..017364a 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -211,4 +211,12 @@
* The assumption is that this method will be called rather infrequently.
*/
void pokeDrawLock(IBinder window);
+
+ /**
+ * Starts a task window move with {startX, startY} as starting point. The amount of move
+ * will be the offset between {startX, startY} and the new cursor position.
+ *
+ * Returns true if the move started successfully; false otherwise.
+ */
+ boolean startMovingTask(IWindow window, float startX, float startY);
}
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 37312d0..e200bef 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -72,6 +72,9 @@
private static final String TAG = LayoutInflater.class.getSimpleName();
private static final boolean DEBUG = false;
+ /** Empty stack trace used to avoid log spam in re-throw exceptions. */
+ private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
+
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -532,15 +535,14 @@
}
} catch (XmlPullParserException e) {
- InflateException ex = new InflateException(e.getMessage());
- ex.initCause(e);
- throw ex;
+ final InflateException ie = new InflateException(e.getMessage(), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
+ throw ie;
} catch (Exception e) {
- InflateException ex = new InflateException(
- parser.getPositionDescription()
- + ": " + e.getMessage());
- ex.initCause(e);
- throw ex;
+ final InflateException ie = new InflateException(parser.getPositionDescription()
+ + ": " + e.getMessage(), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
+ throw ie;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
@@ -625,27 +627,25 @@
return view;
} catch (NoSuchMethodException e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class "
- + (prefix != null ? (prefix + name) : name));
- ie.initCause(e);
+ final InflateException ie = new InflateException(attrs.getPositionDescription()
+ + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (ClassCastException e) {
// If loaded class is not a View subclass
- InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Class is not a View "
- + (prefix != null ? (prefix + name) : name));
- ie.initCause(e);
+ final InflateException ie = new InflateException(attrs.getPositionDescription()
+ + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (ClassNotFoundException e) {
// If loadClass fails, we should propagate the exception.
throw e;
} catch (Exception e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class "
- + (clazz == null ? "<unknown>" : clazz.getName()));
- ie.initCause(e);
+ final InflateException ie = new InflateException(
+ attrs.getPositionDescription() + ": Error inflating class "
+ + (clazz == null ? "<unknown>" : clazz.getName()), e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
@@ -657,8 +657,7 @@
*/
private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
throw new InflateException(attrs.getPositionDescription()
- + ": Class not allowed to be inflated "
- + (prefix != null ? (prefix + name) : name));
+ + ": Class not allowed to be inflated "+ (prefix != null ? (prefix + name) : name));
}
/**
@@ -774,14 +773,14 @@
} catch (ClassNotFoundException e) {
final InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class " + name);
- ie.initCause(e);
+ + ": Error inflating class " + name, e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class " + name);
- ie.initCause(e);
+ + ": Error inflating class " + name, e);
+ ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
}
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 420f7a1..dcef142 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -108,6 +108,11 @@
private Choreographer mChoreographer;
private boolean mRootNodeNeedsUpdate;
+ // In case of multi threaded render nodes, these bounds indicate the content bounds against
+ // which the backdrop needs to be cropped against.
+ private final Rect mCurrentContentBounds = new Rect();
+ private final Rect mStagedContentBounds = new Rect();
+
ThreadedRenderer(Context context, boolean translucent) {
final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
@@ -307,6 +312,47 @@
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
+ /**
+ * Adds a rendernode to the renderer which can be drawn and changed asynchronously to the
+ * rendernode of the UI thread.
+ * @param node The node to add.
+ * @param placeFront If true, the render node will be placed in front of the content node,
+ * otherwise behind the content node.
+ */
+ public void addRenderNode(RenderNode node, boolean placeFront) {
+ nAddRenderNode(mNativeProxy, node.mNativeRenderNode, placeFront);
+ }
+
+ /**
+ * Only especially added render nodes can be removed.
+ * @param node The node which was added via addRenderNode which should get removed again.
+ */
+ public void removeRenderNode(RenderNode node) {
+ nRemoveRenderNode(mNativeProxy, node.mNativeRenderNode);
+ }
+
+ /**
+ * Draws a particular render node. If the node is not the content node, only the additional
+ * nodes will get drawn and the content remains untouched.
+ * @param node The node to be drawn.
+ */
+ public void drawRenderNode(RenderNode node) {
+ nDrawRenderNode(mNativeProxy, node.mNativeRenderNode);
+ }
+
+ /**
+ * To avoid unnecessary overdrawing of the main content all additionally passed render nodes
+ * will be prevented to overdraw this area. It will be synchronized with the draw call.
+ * This should be updated in the content view's draw call.
+ * @param left The left side of the protected bounds.
+ * @param top The top side of the protected bounds.
+ * @param right The right side of the protected bounds.
+ * @param bottom The bottom side of the protected bounds.
+ */
+ public void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+ mStagedContentBounds.set(left, top, right, bottom);
+ }
+
@Override
void invalidateRoot() {
mRootNodeNeedsUpdate = true;
@@ -320,6 +366,14 @@
choreographer.mFrameInfo.markDrawStart();
updateRootDisplayList(view, callbacks);
+ // The main content view was updating the content bounds and we transfer them to the
+ // renderer.
+ if (!mCurrentContentBounds.equals(mStagedContentBounds)) {
+ mCurrentContentBounds.set(mStagedContentBounds);
+ nSetContentOverdrawProtectionBounds(mNativeProxy, mCurrentContentBounds.left,
+ mCurrentContentBounds.top, mCurrentContentBounds.right,
+ mCurrentContentBounds.bottom);
+ }
attachInfo.mIgnoreDirtyState = false;
@@ -541,4 +595,11 @@
private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
@DumpFlags int dumpFlags);
private static native void nDumpProfileData(byte[] data, FileDescriptor fd);
+
+ private static native void nAddRenderNode(long nativeProxy, long rootRenderNode,
+ boolean placeFront);
+ private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode);
+ private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
+ private static native void nSetContentOverdrawProtectionBounds(long nativeProxy, int left,
+ int top, int right, int bottom);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f4fc6e7..263ec7d1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3641,9 +3641,44 @@
* with this flag set, all visible applications will be able to participate
* in the drag operation and receive the dragged content.
*
- * @hide
+ * If this is the only flag set, then the drag recipient will only have access to text data
+ * and intents contained in the {@link ClipData} object. Access to URIs contained in the
+ * {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags.
*/
- public static final int DRAG_FLAG_GLOBAL = 1;
+ public static final int DRAG_FLAG_GLOBAL = 1 << 8; // 256
+
+ /**
+ * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to
+ * request read access to the content URI(s) contained in the {@link ClipData} object.
+ * @see android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
+ */
+ public static final int DRAG_FLAG_GLOBAL_URI_READ = Intent.FLAG_GRANT_READ_URI_PERMISSION;
+
+ /**
+ * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to
+ * request write access to the content URI(s) contained in the {@link ClipData} object.
+ * @see android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ */
+ public static final int DRAG_FLAG_GLOBAL_URI_WRITE = Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+
+ /**
+ * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link
+ * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant can be persisted across device
+ * reboots until explicitly revoked with
+ * {@link android.content.Context#revokeUriPermission(Uri,int) Context.revokeUriPermission}.
+ * @see android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ */
+ public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION =
+ Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
+
+ /**
+ * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link
+ * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant applies to any URI that is a prefix
+ * match against the original granted URI.
+ * @see android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
+ */
+ public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION =
+ Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
/**
* Flag indicating that the drag shadow will be opaque. When
@@ -4671,7 +4706,7 @@
out.append(" #");
out.append(Integer.toHexString(id));
final Resources r = mResources;
- if (Resources.resourceHasPackage(id) && r != null) {
+ if (id > 0 && Resources.resourceHasPackage(id) && r != null) {
try {
String pkgname;
switch (id&0xff000000) {
@@ -5353,7 +5388,7 @@
protected boolean performButtonActionOnTouchDown(MotionEvent event) {
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE &&
(event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
- showContextMenu(event.getX(), event.getY(), event.getMetaState());
+ showContextMenu(event.getX(), event.getY());
mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT;
return true;
}
@@ -5374,13 +5409,10 @@
*
* @param x The referenced x coordinate.
* @param y The referenced y coordinate.
- * @param metaState The keyboard modifiers that were pressed.
* @return Whether a context menu was displayed.
- *
- * @hide
*/
- public boolean showContextMenu(float x, float y, int metaState) {
- return showContextMenu();
+ public boolean showContextMenu(float x, float y) {
+ return getParent().showContextMenuForChild(this, x, y);
}
/**
@@ -7338,6 +7370,23 @@
}
/**
+ * Compute the view's coordinate within the surface.
+ *
+ * <p>Computes the coordinates of this view in its surface. The argument
+ * must be an array of two integers. After the method returns, the array
+ * contains the x and y location in that order.</p>
+ * @hide
+ * @param location an array of two integers in which to hold the coordinates
+ */
+ public void getLocationInSurface(@Size(2) int[] location) {
+ getLocationInWindow(location);
+ if (mAttachInfo != null && mAttachInfo.mViewRootImpl != null) {
+ location[0] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.left;
+ location[1] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.top;
+ }
+ }
+
+ /**
* Provide original WindowInsets that are dispatched to the view hierarchy. The insets are
* only available if the view is attached.
*
@@ -8408,11 +8457,14 @@
*/
public void clearAccessibilityFocus() {
clearAccessibilityFocusNoCallbacks();
- // Clear the global reference of accessibility focus if this
- // view or any of its descendants had accessibility focus.
- ViewRootImpl viewRootImpl = getViewRootImpl();
+
+ // Clear the global reference of accessibility focus if this view or
+ // any of its descendants had accessibility focus. This will NOT send
+ // an event or update internal state if focus is cleared from a
+ // descendant view, which may leave views in inconsistent states.
+ final ViewRootImpl viewRootImpl = getViewRootImpl();
if (viewRootImpl != null) {
- View focusHost = viewRootImpl.getAccessibilityFocusedHost();
+ final View focusHost = viewRootImpl.getAccessibilityFocusedHost();
if (focusHost != null && ViewRootImpl.isViewDescendantOf(focusHost, this)) {
viewRootImpl.setAccessibilityFocus(null, null);
}
@@ -8688,6 +8740,18 @@
public void setImportantForAccessibility(int mode) {
final int oldMode = getImportantForAccessibility();
if (mode != oldMode) {
+ final boolean hideDescendants =
+ mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
+
+ // If this node or its descendants are no longer important, try to
+ // clear accessibility focus.
+ if (mode == IMPORTANT_FOR_ACCESSIBILITY_NO || hideDescendants) {
+ final View focusHost = findAccessibilityFocusHost(hideDescendants);
+ if (focusHost != null) {
+ focusHost.clearAccessibilityFocus();
+ }
+ }
+
// If we're moving between AUTO and another state, we might not need
// to send a subtree changed notification. We'll store the computed
// importance, since we'll need to check it later to make sure.
@@ -8707,6 +8771,31 @@
}
/**
+ * Returns the view within this view's hierarchy that is hosting
+ * accessibility focus.
+ *
+ * @param searchDescendants whether to search for focus in descendant views
+ * @return the view hosting accessibility focus, or {@code null}
+ */
+ private View findAccessibilityFocusHost(boolean searchDescendants) {
+ if (isAccessibilityFocusedViewOrHost()) {
+ return this;
+ }
+
+ if (searchDescendants) {
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ final View focusHost = viewRoot.getAccessibilityFocusedHost();
+ if (focusHost != null && ViewRootImpl.isViewDescendantOf(focusHost, this)) {
+ return focusHost;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
* Computes whether this view should be exposed for accessibility. In
* general, views that are interactive or provide information are exposed
* while views that serve only as containers are hidden.
@@ -19750,6 +19839,26 @@
}
/**
+ * Starts a move from {startX, startY}, the amount of the movement will be the offset
+ * between {startX, startY} and the new cursor positon.
+ * @param startX horizontal coordinate where the move started.
+ * @param startY vertical coordinate where the move started.
+ * @return whether moving was started successfully.
+ * @hide
+ */
+ public final boolean startMovingTask(float startX, float startY) {
+ if (ViewDebug.DEBUG_POSITIONING) {
+ Log.d(VIEW_LOG_TAG, "startMovingTask: {" + startX + "," + startY + "}");
+ }
+ try {
+ return mAttachInfo.mSession.startMovingTask(mAttachInfo.mWindow, startX, startY);
+ } catch (RemoteException e) {
+ Log.e(VIEW_LOG_TAG, "Unable to start moving", e);
+ }
+ return false;
+ }
+
+ /**
* Handles drag events sent by the system following a call to
* {@link android.view.View#startDrag(ClipData,DragShadowBuilder,Object,int) startDrag()}.
*<p>
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8bf53a8..8278335 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -78,6 +78,12 @@
public static final boolean DEBUG_DRAG = false;
/**
+ * Enables detailed logging of task positioning operations.
+ * @hide
+ */
+ public static final boolean DEBUG_POSITIONING = false;
+
+ /**
* This annotation can be used to mark fields and methods to be dumped by
* the view server. Only non-void methods with no arguments can be annotated
* by this annotation.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index bdcd998..475ce2f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -767,6 +767,11 @@
return mParent != null && mParent.showContextMenuForChild(originalView);
}
+ @Override
+ public boolean showContextMenuForChild(View originalView, float x, float y) {
+ return mParent != null && mParent.showContextMenuForChild(originalView, x, y);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 15b86d1..07f1e2c 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -182,6 +182,17 @@
public boolean showContextMenuForChild(View originalView);
/**
+ * Bring up a context menu for the specified view at the given x/y offset from
+ * the top left corner.
+ *
+ * @param originalView
+ * @param x The x offset at which to open the menu
+ * @param y The y offset at which to open the menu
+ * @return true if a context menu was displayed
+ */
+ public boolean showContextMenuForChild(View originalView, float x, float y);
+
+ /**
* Have the parent populate the specified context menu if it has anything to
* add (and then recurse on its parent).
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d26e914..f6c60ed 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -169,6 +169,16 @@
boolean mAppVisible = true;
int mOrigWindowType = -1;
+ /** Whether the window had focus during the most recent traversal. */
+ boolean mHadWindowFocus;
+
+ /**
+ * Whether the window lost focus during a previous traversal and has not
+ * yet gained it back. Used to determine whether a WINDOW_STATE_CHANGE
+ * accessibility events should be sent during traversal.
+ */
+ boolean mLostWindowFocus;
+
// Set to true if the owner of this window is in the stopped state,
// so the window should no longer be active.
boolean mStopped = false;
@@ -191,6 +201,10 @@
Rect mDirty;
boolean mIsAnimating;
+ private boolean mDragResizing;
+ private int mCanvasOffsetX;
+ private int mCanvasOffsetY;
+
CompatibilityInfo.Translator mTranslator;
final View.AttachInfo mAttachInfo;
@@ -576,21 +590,20 @@
// right away, anyway.
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
- throw new WindowManager.BadTokenException(
- "Unable to add window " + mWindow +
- " -- another window of this type already exists");
+ throw new WindowManager.BadTokenException("Unable to add window "
+ + mWindow + " -- another window of type "
+ + mWindowAttributes.type + " already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
- throw new WindowManager.BadTokenException(
- "Unable to add window " + mWindow +
- " -- permission denied for this window type");
+ throw new WindowManager.BadTokenException("Unable to add window "
+ + mWindow + " -- permission denied for window type "
+ + mWindowAttributes.type);
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
- throw new WindowManager.InvalidDisplayException(
- "Unable to add window " + mWindow +
- " -- the specified display can not be found");
+ throw new WindowManager.InvalidDisplayException("Unable to add window "
+ + mWindow + " -- the specified display can not be found");
case WindowManagerGlobal.ADD_INVALID_TYPE:
- throw new WindowManager.InvalidDisplayException(
- "Unable to add window " + mWindow
- + " -- the specified window type is not valid");
+ throw new WindowManager.InvalidDisplayException("Unable to add window "
+ + mWindow + " -- the specified window type "
+ + mWindowAttributes.type + " is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
@@ -1507,6 +1520,7 @@
frame.width() < desiredWindowWidth && frame.width() != mWidth)
|| (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.height() < desiredWindowHeight && frame.height() != mHeight));
+ windowShouldResize |= mDragResizing;
// Determine whether to compute insets.
// If there are no inset listeners remaining then we may still need to compute
@@ -1518,10 +1532,11 @@
boolean insetsPending = false;
int relayoutResult = 0;
+ boolean isViewVisible = viewVisibility == View.VISIBLE;
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null) {
- if (viewVisibility == View.VISIBLE) {
+ if (isViewVisible) {
// If this window is giving internal insets to the window
// manager, and it is being added or changing its visibility,
// then we want to first give the window manager "fake"
@@ -1682,6 +1697,19 @@
return;
}
}
+
+ final boolean dragResizing = (relayoutResult
+ & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0;
+ if (mDragResizing != dragResizing) {
+ mDragResizing = dragResizing;
+ mFullRedrawNeeded = true;
+ }
+ if (dragResizing) {
+ mCanvasOffsetX = mWinFrame.left;
+ mCanvasOffsetY = mWinFrame.top;
+ } else {
+ mCanvasOffsetX = mCanvasOffsetY = 0;
+ }
} catch (RemoteException e) {
}
@@ -1759,10 +1787,6 @@
|| mHeight != hardwareRenderer.getHeight()) {
hardwareRenderer.setup(mWidth, mHeight, mAttachInfo,
mWindowAttributes.surfaceInsets);
- if (!hwInitialized) {
- hardwareRenderer.invalidate(mSurface);
- mFullRedrawNeeded = true;
- }
}
}
@@ -1947,19 +1971,33 @@
mRemainingFrameCount--;
}
+ final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
+ final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
+ final boolean regainedFocus = hasWindowFocus && mLostWindowFocus;
+ if (regainedFocus) {
+ mLostWindowFocus = false;
+ } else if (!hasWindowFocus && mHadWindowFocus) {
+ mLostWindowFocus = true;
+ }
+
+ if (changedVisibility || regainedFocus) {
+ host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+
mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mViewVisibility = viewVisibility;
+ mHadWindowFocus = hasWindowFocus;
- if (mAttachInfo.mHasWindowFocus && !isInLocalFocusMode()) {
+ if (hasWindowFocus && !isInLocalFocusMode()) {
final boolean imTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && imTarget) {
- imm.onPreWindowFocus(mView, true /* hasWindowFocus */);
+ imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
@@ -1972,8 +2010,7 @@
mReportNextDraw = true;
}
- boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() ||
- viewVisibility != View.VISIBLE;
+ boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
@@ -1987,7 +2024,7 @@
performDraw();
}
} else {
- if (viewVisibility == View.VISIBLE) {
+ if (isViewVisible) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
@@ -2464,8 +2501,8 @@
mAttachInfo.mTreeObserver.dispatchOnDraw();
- int xOffset = 0;
- int yOffset = curScrollY;
+ int xOffset = -mCanvasOffsetX;
+ int yOffset = -mCanvasOffsetY + curScrollY;
final WindowManager.LayoutParams params = mWindowAttributes;
final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
if (surfaceInsets != null) {
@@ -3296,13 +3333,6 @@
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
mHasHadWindowFocus = true;
}
-
- if (mView != null && mAccessibilityManager.isEnabled()) {
- if (hasWindowFocus) {
- mView.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- }
- }
}
} break;
case MSG_DIE:
@@ -6210,6 +6240,11 @@
}
@Override
+ public boolean showContextMenuForChild(View originalView, float x, float y) {
+ return false;
+ }
+
+ @Override
public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
return null;
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 5893f4a..0e7089f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -562,28 +562,6 @@
/** Returns the current stack Id for the window. */
int getWindowStackId() throws RemoteException;
-
- /**
- * Returns the bounds of the task that contains this activity.
- *
- * @return Rect The bounds that contains the activity.
- */
- Rect getActivityBounds() throws RemoteException;
-
- /**
- * Sets the bounds (size and position) of the task or stack that contains this
- * activity.
- * NOTE: The requested bounds might not the fully honored by the system depending
- * on the window placement policy.
- *
- * @param newBounds The new target bounds of the activity in task or stack.
- */
- void setActivityBounds(Rect newBounds) throws RemoteException;
-
- /**
- * Activates this activity, hence bringing it to the top and giving it focus.
- */
- void activateActivity() throws RemoteException;
}
public Window(Context context) {
@@ -1197,6 +1175,13 @@
public abstract void addContentView(View view, ViewGroup.LayoutParams params);
/**
+ * Remove the view that was used as the screen content.
+ *
+ * @hide
+ */
+ public abstract void clearContentView();
+
+ /**
* Return the view in this Window that currently has focus, or null if
* there are none. Note that this does not look in any containing
* Window.
@@ -1261,6 +1246,15 @@
public void setElevation(float elevation) {}
/**
+ * Gets the window elevation.
+ *
+ * @hide
+ */
+ public float getElevation() {
+ return 0.0f;
+ }
+
+ /**
* Sets whether window content should be clipped to the outline of the
* window background.
*
@@ -2013,5 +2007,13 @@
*/
public abstract void setNavigationBarColor(@ColorInt int color);
-
+ /**
+ * Get information whether the activity has non client decoration view. These views are used in
+ * the multi window environment, to provide dragging handle and maximize/close buttons.
+ *
+ * @hide
+ */
+ public boolean hasNonClientDecorView() {
+ return false;
+ }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 45bc1df..92e473d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -226,6 +226,7 @@
@ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION, to = "TYPE_PRIVATE_PRESENTATION"),
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION, to = "TYPE_VOICE_INTERACTION"),
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "TYPE_VOICE_INTERACTION_STARTING"),
+ @ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER, to = "TYPE_DOCK_DIVIDER"),
})
public int type;
@@ -565,6 +566,13 @@
public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
/**
+ * Window for displaying a handle used for resizing docked stacks. This window is owned
+ * by the system process.
+ * @hide
+ */
+ public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
+
+ /**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 606168c..ab99b9e 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -70,6 +70,13 @@
public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
/**
+ * The window is being resized by dragging one of the window corners,
+ * in this case the surface would be fullsreen-sized. The client should
+ * render to the actual frame location (instead of (0,curScrollY)).
+ */
+ public static final int RELAYOUT_RES_DRAG_RESIZING = 0x8;
+
+ /**
* Flag for relayout: the client will be later giving
* internal insets; as a result, the window will not impact other window
* layouts until the insets are given.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 057b701..d0c50c9 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -962,6 +962,9 @@
* If the base URL uses any other scheme, then the data will be loaded into
* the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
* entities in the string will not be decoded.
+ * <p>
+ * Note that the baseUrl is sent in the 'Referer' HTTP header when
+ * requesting subresources (images, etc.) of the page loaded using this method.
*
* @param baseUrl the URL to use as the page's base URL. If null defaults to
* 'about:blank'.
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 389fc0a..b8faf0c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2398,6 +2398,7 @@
lp.itemId = mAdapter.getItemId(position);
}
lp.viewType = mAdapter.getItemViewType(position);
+ lp.isEnabled = mAdapter.isEnabled(position);
if (lp != vlp) {
child.setLayoutParams(lp);
}
@@ -2419,19 +2420,33 @@
}
final int position = getPositionForView(host);
- final ListAdapter adapter = getAdapter();
-
- if ((position == INVALID_POSITION) || (adapter == null)) {
+ if (position == INVALID_POSITION || mAdapter == null) {
// Cannot perform actions on invalid items.
return false;
}
- if (!isEnabled() || !adapter.isEnabled(position)) {
- // Cannot perform actions on disabled items.
+ if (position >= mAdapter.getCount()) {
+ // The position is no longer valid, likely due to a data set
+ // change. We could fail here for all data set changes, since
+ // there is a chance that the data bound to the view may no
+ // longer exist at the same position within the adapter, but
+ // it's more consistent with the standard touch interaction to
+ // click at whatever may have moved into that position.
return false;
}
- final long id = getItemIdAtPosition(position);
+ final boolean isItemEnabled;
+ final ViewGroup.LayoutParams lp = host.getLayoutParams();
+ if (lp instanceof AbsListView.LayoutParams) {
+ isItemEnabled = ((AbsListView.LayoutParams) lp).isEnabled;
+ } else {
+ isItemEnabled = false;
+ }
+
+ if (!isEnabled() || !isItemEnabled) {
+ // Cannot perform actions on disabled items.
+ return false;
+ }
switch (action) {
case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
@@ -2447,12 +2462,14 @@
}
} return false;
case AccessibilityNodeInfo.ACTION_CLICK: {
- if (isItemClickable(host, position)) {
+ if (isItemClickable(host)) {
+ final long id = getItemIdAtPosition(position);
return performItemClick(host, position, id);
}
} return false;
case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
if (isLongClickable()) {
+ final long id = getItemIdAtPosition(position);
return performLongPress(host, position, id);
}
} return false;
@@ -2472,13 +2489,20 @@
*/
public void onInitializeAccessibilityNodeInfoForItem(
View view, int position, AccessibilityNodeInfo info) {
- final ListAdapter adapter = getAdapter();
- if (position == INVALID_POSITION || adapter == null) {
+ if (position == INVALID_POSITION) {
// The item doesn't exist, so there's not much we can do here.
return;
}
- if (!isEnabled() || !adapter.isEnabled(position)) {
+ final boolean isItemEnabled;
+ final ViewGroup.LayoutParams lp = view.getLayoutParams();
+ if (lp instanceof AbsListView.LayoutParams) {
+ isItemEnabled = ((AbsListView.LayoutParams) lp).isEnabled;
+ } else {
+ isItemEnabled = false;
+ }
+
+ if (!isEnabled() || !isItemEnabled) {
info.setEnabled(false);
return;
}
@@ -2490,7 +2514,7 @@
info.addAction(AccessibilityAction.ACTION_SELECT);
}
- if (isItemClickable(view, position)) {
+ if (isItemClickable(view)) {
info.addAction(AccessibilityAction.ACTION_CLICK);
info.setClickable(true);
}
@@ -2501,9 +2525,8 @@
}
}
- private boolean isItemClickable(View view, int position) {
- return mAdapter != null && view != null &&
- mAdapter.isEnabled(position) && !view.hasFocusable();
+ private boolean isItemClickable(View view) {
+ return !view.hasFocusable();
}
/**
@@ -3057,6 +3080,15 @@
}
private class CheckForLongPress extends WindowRunnnable implements Runnable {
+ private static final int INVALID_COORD = -1;
+ private float mX = INVALID_COORD;
+ private float mY = INVALID_COORD;
+
+ private void setCoords(float x, float y) {
+ mX = x;
+ mY = y;
+ }
+
@Override
public void run() {
final int motionPosition = mMotionPosition;
@@ -3067,7 +3099,11 @@
boolean handled = false;
if (sameWindow() && !mDataChanged) {
- handled = performLongPress(child, longPressPosition, longPressId);
+ if (mX != INVALID_COORD && mY != INVALID_COORD) {
+ handled = performLongPress(child, longPressPosition, longPressId, mX, mY);
+ } else {
+ handled = performLongPress(child, longPressPosition, longPressId);
+ }
}
if (handled) {
mTouchMode = TOUCH_MODE_REST;
@@ -3123,6 +3159,16 @@
boolean performLongPress(final View child,
final int longPressPosition, final long longPressId) {
+ return performLongPress(
+ child,
+ longPressPosition,
+ longPressId,
+ CheckForLongPress.INVALID_COORD,
+ CheckForLongPress.INVALID_COORD);
+ }
+
+ boolean performLongPress(final View child,
+ final int longPressPosition, final long longPressId, float x, float y) {
// CHOICE_MODE_MULTIPLE_MODAL takes over long press.
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
if (mChoiceActionMode == null &&
@@ -3140,7 +3186,11 @@
}
if (!handled) {
mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
- handled = super.showContextMenuForChild(AbsListView.this);
+ if (x != CheckForLongPress.INVALID_COORD && y != CheckForLongPress.INVALID_COORD) {
+ handled = super.showContextMenuForChild(AbsListView.this, x, y);
+ } else {
+ handled = super.showContextMenuForChild(AbsListView.this);
+ }
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
@@ -3155,17 +3205,17 @@
/** @hide */
@Override
- public boolean showContextMenu(float x, float y, int metaState) {
+ public boolean showContextMenu(float x, float y) {
final int position = pointToPosition((int)x, (int)y);
if (position != INVALID_POSITION) {
final long id = mAdapter.getItemId(position);
View child = getChildAt(position - mFirstPosition);
if (child != null) {
mContextMenuInfo = createContextMenuInfo(child, position, id);
- return super.showContextMenuForChild(AbsListView.this);
+ return super.showContextMenuForChild(AbsListView.this, x, y);
}
}
- return super.showContextMenu(x, y, metaState);
+ return super.showContextMenu(x, y);
}
@Override
@@ -3318,6 +3368,7 @@
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
+ mPendingCheckForLongPress.setCoords(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress, longPressTimeout);
} else {
@@ -6326,6 +6377,9 @@
*/
long itemId = -1;
+ /** Whether the adapter considers the item enabled. */
+ boolean isEnabled;
+
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
@@ -6351,6 +6405,7 @@
encoder.addProperty("list:viewType", viewType);
encoder.addProperty("list:recycledHeaderFooter", recycledHeaderFooter);
encoder.addProperty("list:forceAdd", forceAdd);
+ encoder.addProperty("list:isEnabled", isEnabled);
}
}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 6883db2..68855ff 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -385,17 +385,19 @@
}
@Override
- void onProgressRefresh(float scale, boolean fromUser, int progress) {
- super.onProgressRefresh(scale, fromUser, progress);
+ void onVisualProgressChanged(int id, float scale) {
+ super.onVisualProgressChanged(id, scale);
- final Drawable thumb = mThumb;
- if (thumb != null) {
- setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
+ if (id == R.id.progress) {
+ final Drawable thumb = mThumb;
+ if (thumb != null) {
+ setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
- // Since we draw translated, the drawable's bounds that it signals
- // for invalidation won't be the actual bounds we want invalidated,
- // so just invalidate this whole view.
- invalidate();
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
+ invalidate();
+ }
}
}
@@ -709,8 +711,7 @@
case KeyEvent.KEYCODE_DPAD_RIGHT:
increment = isLayoutRtl() ? -increment : increment;
- // Let progress bar handle clamping values.
- if (setProgress(getProgress() + increment, true)) {
+ if (setProgressInternal(getProgress() + increment, true, true)) {
onKeyChange();
return true;
}
@@ -764,7 +765,7 @@
}
float value = arguments.getFloat(
AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE);
- return setProgress((int) value, true);
+ return setProgressInternal((int) value, true, true);
}
case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
@@ -777,7 +778,7 @@
}
// Let progress bar handle clamping values.
- if (setProgress(getProgress() + increment, true)) {
+ if (setProgressInternal(getProgress() + increment, true, true)) {
onKeyChange();
return true;
}
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 64c4103..2dd84e3 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -85,6 +85,8 @@
private OpenOverflowRunnable mPostedOpenRunnable;
private ActionMenuPopupCallback mPopupCallback;
+ private final boolean mShowCascadingMenus;
+
final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
int mOpenSubMenuId;
@@ -126,6 +128,9 @@
public ActionMenuPresenter(Context context) {
super(context, com.android.internal.R.layout.action_menu_layout,
com.android.internal.R.layout.action_menu_item_layout);
+
+ mShowCascadingMenus = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableCascadingSubmenus);
}
@Override
@@ -500,14 +505,29 @@
}
View anchor = findViewForItem(topSubMenu.getItem());
if (anchor == null) {
- if (mOverflowButton == null) return false;
- anchor = mOverflowButton;
+ // This means the submenu was opened from an overflow menu item, indicating the
+ // MenuPopupHelper will handle opening the submenu via its MenuPopup. Return false to
+ // ensure that the MenuPopup acts as presenter for the submenu, and acts on its
+ // responsibility to display the new submenu.
+ return false;
}
mOpenSubMenuId = subMenu.getItem().getItemId();
- mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
- mActionButtonPopup.setAnchorView(anchor);
+
+ boolean preserveIconSpacing = false;
+ final int count = subMenu.size();
+ for (int i = 0; i < count; i++) {
+ MenuItem childItem = subMenu.getItem(i);
+ if (childItem.isVisible() && childItem.getIcon() != null) {
+ preserveIconSpacing = true;
+ break;
+ }
+ }
+
+ mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu, anchor);
+ mActionButtonPopup.setForceShowIcon(preserveIconSpacing);
mActionButtonPopup.show();
+
super.onSubMenuSelected(subMenu);
return true;
}
@@ -926,12 +946,9 @@
}
private class ActionButtonSubmenu extends MenuPopupHelper {
- private SubMenuBuilder mSubMenu;
-
- public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
- super(context, subMenu, null, false,
+ public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu, View anchorView) {
+ super(context, subMenu, anchorView, false,
com.android.internal.R.attr.actionOverflowMenuStyle);
- mSubMenu = subMenu;
MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
if (!item.isActionButton()) {
@@ -940,17 +957,6 @@
}
setCallback(mPopupPresenterCallback);
-
- boolean preserveIconSpacing = false;
- final int count = subMenu.size();
- for (int i = 0; i < count; i++) {
- MenuItem childItem = subMenu.getItem(i);
- if (childItem.isVisible() && childItem.getIcon() != null) {
- preserveIconSpacing = true;
- break;
- }
- }
- setForceShowIcon(preserveIconSpacing);
}
@Override
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 0cc1b25..6ed7ab8 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -600,13 +600,20 @@
}
/**
- * Get the position within the adapter's data set for the view, where view is a an adapter item
- * or a descendant of an adapter item.
+ * Returns the position within the adapter's data set for the view, where
+ * view is a an adapter item or a descendant of an adapter item.
+ * <p>
+ * <strong>Note:</strong> The result of this method only reflects the
+ * position of the data bound to <var>view</var> during the most recent
+ * layout pass. If the adapter's data set has changed without a subsequent
+ * layout pass, the position returned by this method may not match the
+ * current position of the data within the adapter.
*
- * @param view an adapter item, or a descendant of an adapter item. This must be visible in this
- * AdapterView at the time of the call.
- * @return the position within the adapter's data set of the view, or {@link #INVALID_POSITION}
- * if the view does not correspond to a list item (or it is not currently visible).
+ * @param view an adapter item, or a descendant of an adapter item. This
+ * must be visible in this AdapterView at the time of the call.
+ * @return the position within the adapter's data set of the view, or
+ * {@link #INVALID_POSITION} if the view does not correspond to a
+ * list item (or it is not currently visible)
*/
public int getPositionForView(View view) {
View listItem = view;
@@ -808,6 +815,7 @@
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
dispatchThawSelfOnly(container);
+ handleDataChanged();
}
class AdapterDataSetObserver extends DataSetObserver {
diff --git a/core/java/android/widget/DropDownListView.java b/core/java/android/widget/DropDownListView.java
index 5536513..c869ccb 100644
--- a/core/java/android/widget/DropDownListView.java
+++ b/core/java/android/widget/DropDownListView.java
@@ -25,10 +25,8 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.IntProperty;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
-import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.TextView;
import android.widget.ListView;
@@ -128,6 +126,56 @@
setCacheColorHint(0); // Transparent, since the background drawable could be anything.
}
+ @Override
+ protected boolean shouldShowSelector() {
+ View selectedView = getSelectedView();
+ return selectedView != null && selectedView.isEnabled() || super.shouldShowSelector();
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent ev) {
+ final int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_HOVER_ENTER
+ || action == MotionEvent.ACTION_HOVER_MOVE) {
+ final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
+ if (position != INVALID_POSITION && position != mSelectedPosition) {
+ final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
+ if (hoveredItem.isEnabled()) {
+ // Force a focus so that the proper selector state gets used when we update.
+ requestFocus();
+
+ positionSelector(position, hoveredItem);
+ setSelectedPositionInt(position);
+ setNextSelectedPositionInt(position);
+ }
+ updateSelectorState();
+ }
+ } else {
+ // Do not cancel the selected position if the selection is visible by other reasons.
+ if (!super.shouldShowSelector()) {
+ setSelectedPositionInt(INVALID_POSITION);
+ }
+ }
+ return super.onHoverEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ final int position = pointToPosition(x, y);
+ if (position == INVALID_POSITION) {
+ return super.onTouchEvent(event);
+ }
+
+ if (position != mSelectedPosition) {
+ setSelectedPositionInt(position);
+ setNextSelectedPositionInt(position);
+ }
+
+ return super.onTouchEvent(event);
+ }
+
/**
* Handles forwarded events.
*
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index f994d4a..607e955 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1070,6 +1070,7 @@
child.setLayoutParams(p);
}
p.viewType = mAdapter.getItemViewType(0);
+ p.isEnabled = mAdapter.isEnabled(0);
p.forceAdd = true;
int childHeightSpec = getChildMeasureSpec(
@@ -1480,6 +1481,7 @@
p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
}
p.viewType = mAdapter.getItemViewType(position);
+ p.isEnabled = mAdapter.isEnabled(position);
if (recycled && !p.forceAdd) {
attachViewToParent(child, where, p);
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 3d07d87..b95bc28 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -69,7 +69,6 @@
private static final int EXPAND_LIST_TIMEOUT = 250;
private Context mContext;
- private PopupWindow mPopup;
private ListAdapter mAdapter;
private DropDownListView mDropDownList;
@@ -112,6 +111,8 @@
private int mLayoutDirection;
+ PopupWindow mPopup;
+
/**
* The provided prompt view should appear above list content.
*
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index be83974..53ca6d1 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1200,6 +1200,7 @@
child.setLayoutParams(p);
}
p.viewType = mAdapter.getItemViewType(position);
+ p.isEnabled = mAdapter.isEnabled(position);
p.forceAdd = true;
final int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec,
@@ -1913,6 +1914,7 @@
p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
}
p.viewType = mAdapter.getItemViewType(position);
+ p.isEnabled = mAdapter.isEnabled(position);
if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter
&& p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {
diff --git a/core/java/android/widget/MenuItemHoverListener.java b/core/java/android/widget/MenuItemHoverListener.java
new file mode 100644
index 0000000..87c5c85
--- /dev/null
+++ b/core/java/android/widget/MenuItemHoverListener.java
@@ -0,0 +1,13 @@
+package android.widget;
+
+import com.android.internal.view.menu.MenuBuilder;
+
+/**
+ * An interface notified when a menu item is hovered. Useful for cases when hover should trigger
+ * some behavior at a higher level, like managing the opening and closing of submenus.
+ *
+ * @hide
+ */
+public interface MenuItemHoverListener {
+ public void onItemHovered(MenuBuilder menu, int position);
+}
diff --git a/core/java/android/widget/MenuPopupWindow.java b/core/java/android/widget/MenuPopupWindow.java
index 9e47e85..1fb62d0 100644
--- a/core/java/android/widget/MenuPopupWindow.java
+++ b/core/java/android/widget/MenuPopupWindow.java
@@ -17,10 +17,17 @@
package android.widget;
import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.transition.Transition;
import android.util.AttributeSet;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.view.menu.ListMenuItemView;
+import com.android.internal.view.menu.MenuAdapter;
+import com.android.internal.view.menu.MenuBuilder;
/**
* A MenuPopupWindow represents the popup window for menu.
@@ -30,62 +37,134 @@
*
* @hide
*/
-public class MenuPopupWindow extends ListPopupWindow {
+public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverListener {
+ private MenuItemHoverListener mHoverListener;
+
public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
- return new MenuDropDownListView(context, hijackFocus);
+ MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus);
+ view.setHoverListener(this);
+ return view;
}
- static class MenuDropDownListView extends DropDownListView {
- private boolean mHoveredOnDisabledItem = false;
- private AccessibilityManager mAccessibilityManager;
+ public void setEnterTransition(Transition enterTransition) {
+ mPopup.setEnterTransition(enterTransition);
+ }
- MenuDropDownListView(Context context, boolean hijackFocus) {
+ public void setExitTransition(Transition exitTransition) {
+ mPopup.setExitTransition(exitTransition);
+ }
+
+ public void setHoverListener(MenuItemHoverListener hoverListener) {
+ mHoverListener = hoverListener;
+ }
+
+ /**
+ * Set whether this window is touch modal or if outside touches will be sent to
+ * other windows behind it.
+ */
+ public void setTouchModal(boolean touchModal) {
+ mPopup.setTouchModal(touchModal);
+ }
+
+ @Override
+ public void onItemHovered(MenuBuilder menu, int position) {
+ // Forward up the chain
+ if (mHoverListener != null) {
+ mHoverListener.onItemHovered(menu, position);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static class MenuDropDownListView extends DropDownListView {
+ final int mAdvanceKey;
+ final int mRetreatKey;
+
+ private MenuItemHoverListener mHoverListener;
+
+ public MenuDropDownListView(Context context, boolean hijackFocus) {
super(context, hijackFocus);
- mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+
+ final Resources res = context.getResources();
+ final Configuration config = res.getConfiguration();
+ if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ mAdvanceKey = KeyEvent.KEYCODE_DPAD_LEFT;
+ mRetreatKey = KeyEvent.KEYCODE_DPAD_RIGHT;
+ } else {
+ mAdvanceKey = KeyEvent.KEYCODE_DPAD_RIGHT;
+ mRetreatKey = KeyEvent.KEYCODE_DPAD_LEFT;
+ }
+ }
+
+ public void setHoverListener(MenuItemHoverListener hoverListener) {
+ mHoverListener = hoverListener;
+ }
+
+ public void clearSelection() {
+ setSelectedPositionInt(INVALID_POSITION);
+ setNextSelectedPositionInt(INVALID_POSITION);
}
@Override
- protected boolean shouldShowSelector() {
- return (isHovered() && !mHoveredOnDisabledItem) || super.shouldShowSelector();
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView();
+ if (selectedItem != null && keyCode == mAdvanceKey) {
+ if (selectedItem.isEnabled() &&
+ ((ListMenuItemView) selectedItem).getItemData().hasSubMenu()) {
+ performItemClick(
+ selectedItem,
+ getSelectedItemPosition(),
+ getSelectedItemId());
+ }
+ return true;
+ } else if (selectedItem != null && keyCode == mRetreatKey) {
+ setSelectedPositionInt(INVALID_POSITION);
+ setNextSelectedPositionInt(INVALID_POSITION);
+
+ ((MenuAdapter) getAdapter()).getAdapterMenu().close();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
}
@Override
public boolean onHoverEvent(MotionEvent ev) {
- mHoveredOnDisabledItem = false;
-
- // Accessibility system should already handle hover events and selections, menu does
- // not have to handle it by itself.
- if (mAccessibilityManager.isTouchExplorationEnabled()) {
- return super.onHoverEvent(ev);
- }
+ boolean dispatchHover = false;
+ final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
final int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_HOVER_ENTER
|| action == MotionEvent.ACTION_HOVER_MOVE) {
- final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
if (position != INVALID_POSITION && position != mSelectedPosition) {
final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
if (hoveredItem.isEnabled()) {
- positionSelector(position, hoveredItem);
- setSelectedPositionInt(position);
- } else {
- mHoveredOnDisabledItem = true;
+ dispatchHover = true;
}
- updateSelectorState();
- }
- } else {
- // Do not cancel the selected position if the selection is visible by other reasons.
- if (!super.shouldShowSelector()) {
- setSelectedPositionInt(INVALID_POSITION);
}
}
- return super.onHoverEvent(ev);
+
+ boolean superVal = super.onHoverEvent(ev);
+
+ if (dispatchHover && mHoverListener != null) {
+ ListAdapter adapter = getAdapter();
+ MenuAdapter menuAdapter;
+ if (adapter instanceof HeaderViewListAdapter) {
+ menuAdapter = (MenuAdapter) ((HeaderViewListAdapter) adapter)
+ .getWrappedAdapter();
+ } else {
+ menuAdapter = (MenuAdapter) adapter;
+ }
+
+ mHoverListener.onItemHovered(menuAdapter.getAdapterMenu(), position);
+ }
+
+ return superVal;
}
}
-}
+}
\ No newline at end of file
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index 3b2d60d..34a8439 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -43,6 +43,7 @@
private final MenuBuilder mMenu;
private final View mAnchor;
private final MenuPopupHelper mPopup;
+ private final boolean mShowCascadingMenus;
private OnMenuItemClickListener mMenuItemClickListener;
private OnDismissListener mDismissListener;
@@ -107,6 +108,8 @@
public PopupMenu(Context context, View anchor, int gravity, int popupStyleAttr,
int popupStyleRes) {
mContext = context;
+ mShowCascadingMenus = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableCascadingSubmenus);
mMenu = new MenuBuilder(context);
mMenu.setCallback(this);
mAnchor = anchor;
@@ -267,21 +270,8 @@
* @hide
*/
public boolean onOpenSubMenu(MenuBuilder subMenu) {
- if (subMenu == null) return false;
-
- if (!subMenu.hasVisibleItems()) {
- return true;
- }
-
- // Current menu will be dismissed by the normal helper, submenu will be shown in its place.
- new MenuPopupHelper(mContext, subMenu, mAnchor).show();
- return true;
- }
-
- /**
- * @hide
- */
- public void onCloseSubMenu(SubMenuBuilder menu) {
+ // The menu presenter will handle opening the submenu itself. Nothing to do here.
+ return false;
}
/**
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f9fa027..7b9de79 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -18,6 +18,7 @@
import com.android.internal.R;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -1449,11 +1450,13 @@
anchor.getLocationOnScreen(mScreenLocation);
onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
(mScreenLocation[1] - yoff - displayFrame.top);
- if (onTop) {
- p.gravity = Gravity.LEFT | Gravity.BOTTOM;
- p.y = root.getHeight() - mDrawingLocation[1] + yoff;
- } else {
- p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ if (!mOverlapAnchor) {
+ if (onTop) {
+ p.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ p.y = root.getHeight() - mDrawingLocation[1] + yoff;
+ } else {
+ p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ }
}
}
@@ -1469,13 +1472,21 @@
p.width = Math.min(p.width, displayFrameWidth);
}
- if (onTop) {
- final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
- if (popupTop < 0) {
- p.y += popupTop;
+ if (mOverlapAnchor) {
+ final int displayFrameHeight = displayFrame.bottom - displayFrame.top;
+ final int bottom = p.y + p.height;
+ if (bottom > displayFrame.bottom) {
+ p.y -= bottom - displayFrameHeight;
}
} else {
- p.y = Math.max(p.y, displayFrame.top);
+ if (onTop) {
+ final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
+ if (popupTop < 0) {
+ p.y += popupTop;
+ }
+ } else {
+ p.y = Math.max(p.y, displayFrame.top);
+ }
}
}
@@ -1494,7 +1505,7 @@
* @return The maximum available height for the popup to be completely
* shown.
*/
- public int getMaxAvailableHeight(View anchor) {
+ public int getMaxAvailableHeight(@NonNull View anchor) {
return getMaxAvailableHeight(anchor, 0);
}
@@ -1509,7 +1520,7 @@
* @return The maximum available height for the popup to be completely
* shown.
*/
- public int getMaxAvailableHeight(View anchor, int yOffset) {
+ public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) {
return getMaxAvailableHeight(anchor, yOffset, false);
}
@@ -1527,22 +1538,29 @@
* bottom decorations
* @return The maximum available height for the popup to be completely
* shown.
- *
- * @hide Pending API council approval.
*/
- public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
+ public int getMaxAvailableHeight(
+ @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
final Rect displayFrame = new Rect();
anchor.getWindowVisibleDisplayFrame(displayFrame);
final int[] anchorPos = mDrawingLocation;
anchor.getLocationOnScreen(anchorPos);
- int bottomEdge = displayFrame.bottom;
+ final int bottomEdge;
if (ignoreBottomDecorations) {
- Resources res = anchor.getContext().getResources();
+ final Resources res = anchor.getContext().getResources();
bottomEdge = res.getDisplayMetrics().heightPixels;
+ } else {
+ bottomEdge = displayFrame.bottom;
}
- final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
+
+ final int distanceToBottom;
+ if (mOverlapAnchor) {
+ distanceToBottom = bottomEdge - anchorPos[1] - yOffset;
+ } else {
+ distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
+ }
final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
// anchorPos[1] is distance from anchor to top of screen
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index fce3754..04c68ae 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -16,10 +16,13 @@
package android.widget;
+import android.animation.ObjectAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.PorterDuff;
+import android.util.FloatProperty;
+import android.util.IntProperty;
import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;
@@ -28,7 +31,6 @@
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Shader;
@@ -38,7 +40,6 @@
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.shapes.Shape;
@@ -57,6 +58,7 @@
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;
@@ -198,9 +200,17 @@
*/
@RemoteView
public class ProgressBar extends View {
+
private static final int MAX_LEVEL = 10000;
private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200;
+ /** Interpolator used for smooth progress animations. */
+ private static final DecelerateInterpolator PROGRESS_ANIM_INTERPOLATOR =
+ new DecelerateInterpolator();
+
+ /** Duration of smooth progress animations. */
+ private static final int PROGRESS_ANIM_DURATION = 80;
+
int mMinWidth;
int mMaxWidth;
int mMinHeight;
@@ -234,6 +244,9 @@
private boolean mAttached;
private boolean mRefreshIsPosted;
+ /** Value used to track progress animation, in the range [0...1]. */
+ private float mVisualProgress;
+
boolean mMirrorForRtl = false;
private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
@@ -814,8 +827,8 @@
updateDrawableBounds(getWidth(), getHeight());
updateDrawableState();
- doRefreshProgress(R.id.progress, mProgress, false, false);
- doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false);
+ doRefreshProgress(R.id.progress, mProgress, false, false, false);
+ doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false, false);
}
}
@@ -1246,7 +1259,7 @@
final int count = mRefreshData.size();
for (int i = 0; i < count; i++) {
final RefreshData rd = mRefreshData.get(i);
- doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
+ doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate);
rd.recycle();
}
mRefreshData.clear();
@@ -1263,8 +1276,9 @@
public int id;
public int progress;
public boolean fromUser;
+ public boolean animate;
- public static RefreshData obtain(int id, int progress, boolean fromUser) {
+ public static RefreshData obtain(int id, int progress, boolean fromUser, boolean animate) {
RefreshData rd = sPool.acquire();
if (rd == null) {
rd = new RefreshData();
@@ -1272,6 +1286,7 @@
rd.id = id;
rd.progress = progress;
rd.fromUser = fromUser;
+ rd.animate = animate;
return rd;
}
@@ -1281,26 +1296,21 @@
}
private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
- boolean callBackToApp) {
- float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
- final Drawable d = mCurrentDrawable;
- if (d != null) {
- Drawable progressDrawable = null;
+ boolean callBackToApp, boolean animate) {
+ final float scale = mMax > 0 ? progress / (float) mMax : 0;
+ final boolean isPrimary = id == R.id.progress;
- if (d instanceof LayerDrawable) {
- progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id);
- if (progressDrawable != null && canResolveLayoutDirection()) {
- progressDrawable.setLayoutDirection(getLayoutDirection());
- }
- }
-
- final int level = (int) (scale * MAX_LEVEL);
- (progressDrawable != null ? progressDrawable : d).setLevel(level);
+ if (isPrimary && animate) {
+ final ObjectAnimator animator = ObjectAnimator.ofFloat(this, VISUAL_PROGRESS, scale);
+ animator.setAutoCancel(true);
+ animator.setDuration(PROGRESS_ANIM_DURATION);
+ animator.setInterpolator(PROGRESS_ANIM_INTERPOLATOR);
+ animator.start();
} else {
- invalidate();
+ setVisualProgress(id, scale);
}
- if (callBackToApp && id == R.id.progress) {
+ if (isPrimary && callBackToApp) {
onProgressRefresh(scale, fromUser, progress);
}
}
@@ -1311,15 +1321,51 @@
}
}
- private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
+ /**
+ * Sets the visual state of a progress indicator.
+ *
+ * @param id the identifier of the progress indicator
+ * @param progress the visual progress in the range [0...1]
+ */
+ private void setVisualProgress(int id, float progress) {
+ mVisualProgress = progress;
+
+ Drawable d = mCurrentDrawable;
+
+ if (d instanceof LayerDrawable) {
+ d = ((LayerDrawable) d).findDrawableByLayerId(id);
+ }
+
+ if (d != null) {
+ final int level = (int) (progress * MAX_LEVEL);
+ d.setLevel(level);
+ } else {
+ invalidate();
+ }
+
+ onVisualProgressChanged(id, progress);
+ }
+
+ /**
+ * Called when the visual state of a progress indicator changes.
+ *
+ * @param id the identifier of the progress indicator
+ * @param progress the visual progress in the range [0...1]
+ */
+ void onVisualProgressChanged(int id, float progress) {
+ // Stub method.
+ }
+
+ private synchronized void refreshProgress(int id, int progress, boolean fromUser,
+ boolean animate) {
if (mUiThreadId == Thread.currentThread().getId()) {
- doRefreshProgress(id, progress, fromUser, true);
+ doRefreshProgress(id, progress, fromUser, true, animate);
} else {
if (mRefreshProgressRunnable == null) {
mRefreshProgressRunnable = new RefreshProgressRunnable();
}
- final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
+ final RefreshData rd = RefreshData.obtain(id, progress, fromUser, animate);
mRefreshData.add(rd);
if (mAttached && !mRefreshIsPosted) {
post(mRefreshProgressRunnable);
@@ -1329,8 +1375,8 @@
}
/**
- * <p>Set the current progress to the specified value. Does not do anything
- * if the progress bar is in indeterminate mode.</p>
+ * Sets the current progress to the specified value. Does not do anything
+ * if the progress bar is in indeterminate mode.
*
* @param progress the new progress, between 0 and {@link #getMax()}
*
@@ -1341,11 +1387,26 @@
*/
@android.view.RemotableViewMethod
public synchronized void setProgress(int progress) {
- setProgress(progress, false);
+ setProgressInternal(progress, false, false);
+ }
+
+ /**
+ * Sets the current progress to the specified value, optionally animating
+ * between the current and target values.
+ * <p>
+ * Animation does not affect the result of {@link #getProgress()}, which
+ * will return the target value immediately after this method is called.
+ *
+ * @param progress the new progress value, between 0 and {@link #getMax()}
+ * @param animate {@code true} to animate between the current and target
+ * values or {@code false} to not animate
+ */
+ public void setProgress(int progress, boolean animate) {
+ setProgressInternal(progress, false, animate);
}
@android.view.RemotableViewMethod
- synchronized boolean setProgress(int progress, boolean fromUser) {
+ synchronized boolean setProgressInternal(int progress, boolean fromUser, boolean animate) {
if (mIndeterminate) {
// Not applicable.
return false;
@@ -1359,7 +1420,7 @@
}
mProgress = progress;
- refreshProgress(R.id.progress, mProgress, fromUser);
+ refreshProgress(R.id.progress, mProgress, fromUser, animate);
return true;
}
@@ -1391,7 +1452,7 @@
if (secondaryProgress != mSecondaryProgress) {
mSecondaryProgress = secondaryProgress;
- refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false);
+ refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false);
}
}
@@ -1464,7 +1525,7 @@
if (mProgress > max) {
mProgress = max;
}
- refreshProgress(R.id.progress, mProgress, false);
+ refreshProgress(R.id.progress, mProgress, false, false);
}
}
@@ -1847,7 +1908,7 @@
final int count = mRefreshData.size();
for (int i = 0; i < count; i++) {
final RefreshData rd = mRefreshData.get(i);
- doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
+ doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate);
rd.recycle();
}
mRefreshData.clear();
@@ -1956,4 +2017,23 @@
boolean mHasSecondaryProgressTint;
boolean mHasSecondaryProgressTintMode;
}
+
+ /**
+ * Property wrapper around the visual state of the {@code progress} functionality
+ * handled by the {@link ProgressBar#setProgress(int, boolean)} method. This does
+ * not correspond directly to the actual progress -- only the visual state.
+ */
+ private final FloatProperty<ProgressBar> VISUAL_PROGRESS =
+ new FloatProperty<ProgressBar>("visual_progress") {
+ @Override
+ public void setValue(ProgressBar object, float value) {
+ object.setVisualProgress(R.id.progress, value);
+ object.mVisualProgress = value;
+ }
+
+ @Override
+ public Float get(ProgressBar object) {
+ return object.mVisualProgress;
+ }
+ };
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7ca3339..ca1b211 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -31,6 +31,7 @@
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.Rect;
@@ -55,6 +56,8 @@
import android.widget.AdapterView.OnItemClickListener;
import libcore.util.Objects;
+import com.android.internal.R;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -206,14 +209,22 @@
/** @hide */
public static class OnClickHandler {
+
+ private int mEnterAnimationId;
+
public boolean onClickHandler(View view, PendingIntent pendingIntent,
Intent fillInIntent) {
try {
// TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Context context = view.getContext();
- ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
- 0, 0,
- view.getMeasuredWidth(), view.getMeasuredHeight());
+ ActivityOptions opts;
+ if (mEnterAnimationId != 0) {
+ opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
+ } else {
+ opts = ActivityOptions.makeScaleUpAnimation(view,
+ 0, 0,
+ view.getMeasuredWidth(), view.getMeasuredHeight());
+ }
context.startIntentSender(
pendingIntent.getIntentSender(), fillInIntent,
Intent.FLAG_ACTIVITY_NEW_TASK,
@@ -228,6 +239,10 @@
}
return true;
}
+
+ public void setEnterAnimationId(int enterAnimationId) {
+ mEnterAnimationId = enterAnimationId;
+ }
}
/**
@@ -2761,11 +2776,31 @@
inflater.setFilter(this);
result = inflater.inflate(rvToApply.getLayoutId(), parent, false);
+ loadTransitionOverride(context, handler);
+
rvToApply.performApply(result, parent, handler);
return result;
}
+ private static void loadTransitionOverride(Context context,
+ RemoteViews.OnClickHandler handler) {
+ if (handler != null && context.getResources().getBoolean(
+ com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
+ TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ int windowAnimations = windowStyle.getResourceId(
+ com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+ TypedArray windowAnimationStyle = context.obtainStyledAttributes(
+ windowAnimations, com.android.internal.R.styleable.WindowAnimation);
+ handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
+ com.android.internal.R.styleable.
+ WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
+ windowStyle.recycle();
+ windowAnimationStyle.recycle();
+ }
+ }
+
/**
* Applies all of the actions to the provided view.
*
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 6f3a711..434516d 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1387,7 +1387,7 @@
mTrackDrawable.jumpToCurrentState();
}
- if (mPositionAnimator != null && mPositionAnimator.isRunning()) {
+ if (mPositionAnimator != null && mPositionAnimator.isStarted()) {
mPositionAnimator.end();
mPositionAnimator = null;
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 2365b48..61ef6dc 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -22,7 +22,6 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.SpannableStringBuilder;
@@ -32,7 +31,6 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.StateSet;
-import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -66,10 +64,8 @@
// Also NOT a real index, just used for keyboard mode.
private static final int ENABLE_PICKER_INDEX = 3;
- private static final int[] ATTRS_TEXT_COLOR = new int[] {
- com.android.internal.R.attr.textColor};
- private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
- com.android.internal.R.attr.disabledAlpha};
+ private static final int[] ATTRS_TEXT_COLOR = new int[] {R.attr.textColor};
+ private static final int[] ATTRS_DISABLED_ALPHA = new int[] {R.attr.disabledAlpha};
// LayoutLib relies on these constants. Change TimePickerClockDelegate_Delegate if
// modifying these.
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 471ea9b..acbf5eb 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -98,6 +98,32 @@
* <p>In modern Android UIs developers should lean more on a visually distinct color scheme for
* toolbars than on their application icon. The use of application icon plus title as a standard
* layout is discouraged on API 21 devices and newer.</p>
+ *
+ * @attr ref android.R.styleable#Toolbar_buttonGravity
+ * @attr ref android.R.styleable#Toolbar_collapseContentDescription
+ * @attr ref android.R.styleable#Toolbar_collapseIcon
+ * @attr ref android.R.styleable#Toolbar_contentInsetEnd
+ * @attr ref android.R.styleable#Toolbar_contentInsetLeft
+ * @attr ref android.R.styleable#Toolbar_contentInsetRight
+ * @attr ref android.R.styleable#Toolbar_contentInsetStart
+ * @attr ref android.R.styleable#Toolbar_gravity
+ * @attr ref android.R.styleable#Toolbar_logo
+ * @attr ref android.R.styleable#Toolbar_logoDescription
+ * @attr ref android.R.styleable#Toolbar_maxButtonHeight
+ * @attr ref android.R.styleable#Toolbar_navigationContentDescription
+ * @attr ref android.R.styleable#Toolbar_navigationIcon
+ * @attr ref android.R.styleable#Toolbar_popupTheme
+ * @attr ref android.R.styleable#Toolbar_subtitle
+ * @attr ref android.R.styleable#Toolbar_subtitleTextAppearance
+ * @attr ref android.R.styleable#Toolbar_subtitleTextColor
+ * @attr ref android.R.styleable#Toolbar_title
+ * @attr ref android.R.styleable#Toolbar_titleMargin
+ * @attr ref android.R.styleable#Toolbar_titleMarginBottom
+ * @attr ref android.R.styleable#Toolbar_titleMarginEnd
+ * @attr ref android.R.styleable#Toolbar_titleMarginStart
+ * @attr ref android.R.styleable#Toolbar_titleMarginTop
+ * @attr ref android.R.styleable#Toolbar_titleTextAppearance
+ * @attr ref android.R.styleable#Toolbar_titleTextColor
*/
public class Toolbar extends ViewGroup {
private static final String TAG = "Toolbar";
@@ -203,7 +229,7 @@
mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity);
mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
- a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0);
+ a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargin, 0);
final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1);
if (marginStart >= 0) {
@@ -321,6 +347,116 @@
return mPopupTheme;
}
+ /**
+ * Sets the title margin.
+ *
+ * @param start the starting title margin in pixels
+ * @param top the top title margin in pixels
+ * @param end the ending title margin in pixels
+ * @param bottom the bottom title margin in pixels
+ * @see #getTitleMarginStart()
+ * @see #getTitleMarginTop()
+ * @see #getTitleMarginEnd()
+ * @see #getTitleMarginBottom()
+ * @attr ref android.R.styleable#Toolbar_titleMargin
+ */
+ public void setTitleMargin(int start, int top, int end, int bottom) {
+ mTitleMarginStart = start;
+ mTitleMarginTop = top;
+ mTitleMarginEnd = end;
+ mTitleMarginBottom = bottom;
+
+ requestLayout();
+ }
+
+ /**
+ * @return the starting title margin in pixels
+ * @see #setTitleMarginStart(int)
+ * @attr ref android.R.styleable#Toolbar_titleMarginStart
+ */
+ public int getTitleMarginStart() {
+ return mTitleMarginStart;
+ }
+
+ /**
+ * Sets the starting title margin in pixels.
+ *
+ * @param margin the starting title margin in pixels
+ * @see #getTitleMarginStart()
+ * @attr ref android.R.styleable#Toolbar_titleMarginStart
+ */
+ public void setTitleMarginStart(int margin) {
+ mTitleMarginStart = margin;
+
+ requestLayout();
+ }
+
+ /**
+ * @return the top title margin in pixels
+ * @see #setTitleMarginTop(int)
+ * @attr ref android.R.styleable#Toolbar_titleMarginTop
+ */
+ public int getTitleMarginTop() {
+ return mTitleMarginTop;
+ }
+
+ /**
+ * Sets the top title margin in pixels.
+ *
+ * @param margin the top title margin in pixels
+ * @see #getTitleMarginTop()
+ * @attr ref android.R.styleable#Toolbar_titleMarginTop
+ */
+ public void setTitleMarginTop(int margin) {
+ mTitleMarginTop = margin;
+
+ requestLayout();
+ }
+
+ /**
+ * @return the ending title margin in pixels
+ * @see #setTitleMarginEnd(int)
+ * @attr ref android.R.styleable#Toolbar_titleMarginEnd
+ */
+ public int getTitleMarginEnd() {
+ return mTitleMarginEnd;
+ }
+
+ /**
+ * Sets the ending title margin in pixels.
+ *
+ * @param margin the ending title margin in pixels
+ * @see #getTitleMarginEnd()
+ * @attr ref android.R.styleable#Toolbar_titleMarginEnd
+ */
+ public void setTitleMarginEnd(int margin) {
+ mTitleMarginEnd = margin;
+
+ requestLayout();
+ }
+
+ /**
+ * @return the bottom title margin in pixels
+ * @see #setTitleMarginBottom(int)
+ * @attr ref android.R.styleable#Toolbar_titleMarginBottom
+ */
+ public int getTitleMarginBottom() {
+ return mTitleMarginBottom;
+ }
+
+ /**
+ * Sets the bottom title margin in pixels.
+ *
+ * @param margin the bottom title margin in pixels
+ * @see #getTitleMarginBottom()
+ * @attr ref android.R.styleable#Toolbar_titleMarginBottom
+ */
+ public void setTitleMarginBottom(int margin) {
+ mTitleMarginBottom = margin;
+
+ requestLayout();
+ }
+
@Override
public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5fc8c7a..2172b5c 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -104,6 +104,7 @@
sri.resultTargets);
}
unbindService(sri.connection);
+ sri.connection.destroy();
mServiceConnections.remove(sri.connection);
if (mServiceConnections.isEmpty()) {
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
@@ -208,6 +209,8 @@
mRefinementResultReceiver.destroy();
mRefinementResultReceiver = null;
}
+ unbindRemainingServices();
+ mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
}
@Override
@@ -265,6 +268,11 @@
return true;
}
+ @Override
+ boolean shouldAutoLaunchSingleChoice() {
+ return false;
+ }
+
private void modifyTargetIntent(Intent in) {
final String action = in.getAction();
if (Intent.ACTION_SEND.equals(action) ||
@@ -371,7 +379,8 @@
continue;
}
- final ChooserTargetServiceConnection conn = new ChooserTargetServiceConnection(dri);
+ final ChooserTargetServiceConnection conn =
+ new ChooserTargetServiceConnection(this, dri);
if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
UserHandle.CURRENT)) {
if (DEBUG) {
@@ -425,6 +434,7 @@
final ChooserTargetServiceConnection conn = mServiceConnections.get(i);
if (DEBUG) Log.d(TAG, "unbinding " + conn);
unbindService(conn);
+ conn.destroy();
}
mServiceConnections.clear();
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
@@ -637,7 +647,8 @@
@Override
public CharSequence getExtendedInfo() {
- return mSourceInfo != null ? mSourceInfo.getExtendedInfo() : null;
+ // ChooserTargets have badge icons, so we won't show the extended info to disambiguate.
+ return null;
}
@Override
@@ -730,9 +741,8 @@
@Override
public boolean showsExtendedInfo(TargetInfo info) {
- // Reserve space to show extended info if any one of the items in the adapter has
- // extended info. This keeps grid item sizes uniform.
- return hasExtendedInfo();
+ // We have badges so we don't need this text shown.
+ return false;
}
@Override
@@ -1024,54 +1034,93 @@
}
}
- class ChooserTargetServiceConnection implements ServiceConnection {
+ static class ChooserTargetServiceConnection implements ServiceConnection {
private final DisplayResolveInfo mOriginalTarget;
+ private ComponentName mConnectedComponent;
+ private ChooserActivity mChooserActivity;
+ private final Object mLock = new Object();
private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() {
@Override
public void sendResult(List<ChooserTarget> targets) throws RemoteException {
- filterServiceTargets(mOriginalTarget.getResolveInfo().activityInfo.packageName,
- targets);
- final Message msg = Message.obtain();
- msg.what = CHOOSER_TARGET_SERVICE_RESULT;
- msg.obj = new ServiceResultInfo(mOriginalTarget, targets,
- ChooserTargetServiceConnection.this);
- mChooserHandler.sendMessage(msg);
+ synchronized (mLock) {
+ if (mChooserActivity == null) {
+ Log.e(TAG, "destroyed ChooserTargetServiceConnection received result from "
+ + mConnectedComponent + "; ignoring...");
+ return;
+ }
+ mChooserActivity.filterServiceTargets(
+ mOriginalTarget.getResolveInfo().activityInfo.packageName, targets);
+ final Message msg = Message.obtain();
+ msg.what = CHOOSER_TARGET_SERVICE_RESULT;
+ msg.obj = new ServiceResultInfo(mOriginalTarget, targets,
+ ChooserTargetServiceConnection.this);
+ mChooserActivity.mChooserHandler.sendMessage(msg);
+ }
}
};
- public ChooserTargetServiceConnection(DisplayResolveInfo dri) {
+ public ChooserTargetServiceConnection(ChooserActivity chooserActivity,
+ DisplayResolveInfo dri) {
+ mChooserActivity = chooserActivity;
mOriginalTarget = dri;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Log.d(TAG, "onServiceConnected: " + name);
- final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service);
- try {
- icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(),
- mOriginalTarget.getResolveInfo().filter, mChooserTargetResult);
- } catch (RemoteException e) {
- Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
- unbindService(this);
- mServiceConnections.remove(this);
+ synchronized (mLock) {
+ if (mChooserActivity == null) {
+ Log.e(TAG, "destroyed ChooserTargetServiceConnection got onServiceConnected");
+ return;
+ }
+
+ final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service);
+ try {
+ icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(),
+ mOriginalTarget.getResolveInfo().filter, mChooserTargetResult);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
+ mChooserActivity.unbindService(this);
+ destroy();
+ mChooserActivity.mServiceConnections.remove(this);
+ }
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name);
- unbindService(this);
- mServiceConnections.remove(this);
- if (mServiceConnections.isEmpty()) {
- mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
- sendVoiceChoicesIfNeeded();
+ synchronized (mLock) {
+ if (mChooserActivity == null) {
+ Log.e(TAG,
+ "destroyed ChooserTargetServiceConnection got onServiceDisconnected");
+ return;
+ }
+
+ mChooserActivity.unbindService(this);
+ destroy();
+ mChooserActivity.mServiceConnections.remove(this);
+ if (mChooserActivity.mServiceConnections.isEmpty()) {
+ mChooserActivity.mChooserHandler.removeMessages(
+ CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
+ mChooserActivity.sendVoiceChoicesIfNeeded();
+ }
+ mConnectedComponent = null;
+ }
+ }
+
+ public void destroy() {
+ synchronized (mLock) {
+ mChooserActivity = null;
}
}
@Override
public String toString() {
- return mOriginalTarget.getResolveInfo().activityInfo.toString();
+ return "ChooserTargetServiceConnection{service="
+ + mConnectedComponent + ", activity="
+ + mOriginalTarget.getResolveInfo().activityInfo.toString() + "}";
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7dd3bed..ef9d1ce 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -234,7 +234,9 @@
mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
- configureContentView(mIntents, initialIntents, rList, alwaysUseOption);
+ if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {
+ return;
+ }
// Prevent the Resolver window from becoming the top fullscreen window and thus from taking
// control of the system bars.
@@ -794,6 +796,10 @@
return false;
}
+ boolean shouldAutoLaunchSingleChoice() {
+ return true;
+ }
+
void showAppDetails(ResolveInfo ri) {
Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
@@ -808,7 +814,10 @@
launchedFromUid, filterLastUsed);
}
- void configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
+ /**
+ * Returns true if the activity is finishing and creation should halt
+ */
+ boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
List<ResolveInfo> rList, boolean alwaysUseOption) {
// The last argument of createAdapter is whether to do special handling
// of the last used choice to highlight it in the list. We need to always
@@ -828,7 +837,9 @@
mAlwaysUseOption = alwaysUseOption;
int count = mAdapter.getUnfilteredCount();
- if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
+ if ((!shouldAutoLaunchSingleChoice() && count > 0)
+ || count > 1
+ || (count == 1 && mAdapter.getOtherProfile() != null)) {
setContentView(layoutId);
mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption);
@@ -837,7 +848,7 @@
mPackageMonitor.unregister();
mRegistered = false;
finish();
- return;
+ return true;
} else {
setContentView(R.layout.resolver_list);
@@ -847,6 +858,7 @@
mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
mAdapterView.setVisibility(View.GONE);
}
+ return false;
}
void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter,
@@ -884,6 +896,7 @@
private final ResolveInfo mResolveInfo;
private final CharSequence mDisplayLabel;
private Drawable mDisplayIcon;
+ private Drawable mBadge;
private final CharSequence mExtendedInfo;
private final Intent mResolvedIntent;
private final List<Intent> mSourceIntents = new ArrayList<>();
@@ -928,7 +941,25 @@
}
public Drawable getBadgeIcon() {
- return null;
+ // We only expose a badge if we have extended info.
+ // The badge is a higher-priority disambiguation signal
+ // but we don't need one if we wouldn't show extended info at all.
+ if (TextUtils.isEmpty(getExtendedInfo())) {
+ return null;
+ }
+
+ if (mBadge == null && mResolveInfo != null && mResolveInfo.activityInfo != null
+ && mResolveInfo.activityInfo.applicationInfo != null) {
+ if (mResolveInfo.activityInfo.icon == 0 || mResolveInfo.activityInfo.icon
+ == mResolveInfo.activityInfo.applicationInfo.icon) {
+ // Badging an icon with exactly the same icon is silly.
+ // If the activityInfo icon resid is 0 it will fall back
+ // to the application's icon, making it a match.
+ return null;
+ }
+ mBadge = mResolveInfo.activityInfo.applicationInfo.loadIcon(mPm);
+ }
+ return mBadge;
}
@Override
@@ -1366,8 +1397,8 @@
} else {
mHasExtendedInfo = true;
boolean usePkg = false;
- CharSequence startApp = ro.getResolveInfoAt(0).activityInfo.applicationInfo
- .loadLabel(mPm);
+ final ApplicationInfo ai = ro.getResolveInfoAt(0).activityInfo.applicationInfo;
+ final CharSequence startApp = ai.loadLabel(mPm);
if (startApp == null) {
usePkg = true;
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index a4ef00a..01ac22e 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -793,6 +793,24 @@
return imeMap;
}
+ @NonNull
+ public static String buildInputMethodsAndSubtypesString(
+ @NonNull final ArrayMap<String, ArraySet<String>> map) {
+ // we want to use the canonical InputMethodSettings implementation,
+ // so we convert data structures first.
+ List<Pair<String, ArrayList<String>>> imeMap = new ArrayList<>(4);
+ for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
+ final String imeName = entry.getKey();
+ final ArraySet<String> subtypeSet = entry.getValue();
+ final ArrayList<String> subtypes = new ArrayList<>(2);
+ if (subtypeSet != null) {
+ subtypes.addAll(subtypeSet);
+ }
+ imeMap.add(new Pair<>(imeName, subtypes));
+ }
+ return InputMethodSettings.buildInputMethodsSettingString(imeMap);
+ }
+
/**
* Utility class for putting and getting settings for InputMethod
* TODO: Move all putters and getters of settings to this class.
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 6da0f63..b6240e4 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -44,6 +44,8 @@
public static final int ACTION_FINGERPRINT_AUTH = 252;
public static final int ACTION_FINGERPRINT_DELETE = 253;
public static final int ACTION_FINGERPRINT_RENAME = 254;
+ public static final int ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255;
+ public static final int ACTION_WIGGLE_CAMERA_GESTURE = 256;
public static void visible(Context context, int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 4f4d3e0..f178c8c 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -338,7 +338,7 @@
}
if (mCpuPowerCalculator == null) {
- mCpuPowerCalculator = new CpuPowerCalculator();
+ mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
}
mCpuPowerCalculator.reset();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4ff7869..8cf2dabdb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 130 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 132 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -118,8 +118,6 @@
// in to one common name.
private static final int MAX_WAKELOCKS_PER_UID = 100;
- private static int sNumSpeedSteps;
-
private final JournaledFile mFile;
public final AtomicFile mCheckinFile;
public final AtomicFile mDailyFile;
@@ -133,7 +131,7 @@
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
- private final KernelCpuSpeedReader mKernelCpuSpeedReader = new KernelCpuSpeedReader();
+ private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
@@ -4411,7 +4409,7 @@
LongSamplingCounter mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase);
LongSamplingCounter mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase);
LongSamplingCounter mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase);
- LongSamplingCounter[] mSpeedBins;
+ LongSamplingCounter[][] mCpuClusterSpeed;
/**
* The statistics we have collected for this uid's wake locks.
@@ -4470,7 +4468,6 @@
mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
mWifiMulticastTimers, mOnBatteryTimeBase);
mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE];
- mSpeedBins = new LongSamplingCounter[getCpuSpeedSteps()];
}
@Override
@@ -5008,10 +5005,18 @@
}
@Override
- public long getTimeAtCpuSpeed(int step, int which) {
- if (step >= 0 && step < mSpeedBins.length) {
- if (mSpeedBins[step] != null) {
- return mSpeedBins[step].getCountLocked(which);
+ public long getTimeAtCpuSpeed(int cluster, int step, int which) {
+ if (mCpuClusterSpeed != null) {
+ if (cluster >= 0 && cluster < mCpuClusterSpeed.length) {
+ final LongSamplingCounter[] cpuSpeeds = mCpuClusterSpeed[cluster];
+ if (cpuSpeeds != null) {
+ if (step >= 0 && step < cpuSpeeds.length) {
+ final LongSamplingCounter c = cpuSpeeds[step];
+ if (c != null) {
+ return c.getCountLocked(which);
+ }
+ }
+ }
}
}
return 0;
@@ -5128,10 +5133,16 @@
mUserCpuTime.reset(false);
mSystemCpuTime.reset(false);
mCpuPower.reset(false);
- for (int i = 0; i < mSpeedBins.length; i++) {
- LongSamplingCounter c = mSpeedBins[i];
- if (c != null) {
- c.reset(false);
+
+ if (mCpuClusterSpeed != null) {
+ for (LongSamplingCounter[] speeds : mCpuClusterSpeed) {
+ if (speeds != null) {
+ for (LongSamplingCounter speed : speeds) {
+ if (speed != null) {
+ speed.reset(false);
+ }
+ }
+ }
}
}
@@ -5280,10 +5291,16 @@
mUserCpuTime.detach();
mSystemCpuTime.detach();
mCpuPower.detach();
- for (int i = 0; i < mSpeedBins.length; i++) {
- LongSamplingCounter c = mSpeedBins[i];
- if (c != null) {
- c.detach();
+
+ if (mCpuClusterSpeed != null) {
+ for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
+ if (cpuSpeeds != null) {
+ for (LongSamplingCounter c : cpuSpeeds) {
+ if (c != null) {
+ c.detach();
+ }
+ }
+ }
}
}
}
@@ -5461,15 +5478,27 @@
mSystemCpuTime.writeToParcel(out);
mCpuPower.writeToParcel(out);
- out.writeInt(mSpeedBins.length);
- for (int i = 0; i < mSpeedBins.length; i++) {
- LongSamplingCounter c = mSpeedBins[i];
- if (c != null) {
- out.writeInt(1);
- c.writeToParcel(out);
- } else {
- out.writeInt(0);
+ if (mCpuClusterSpeed != null) {
+ out.writeInt(1);
+ out.writeInt(mCpuClusterSpeed.length);
+ for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
+ if (cpuSpeeds != null) {
+ out.writeInt(1);
+ out.writeInt(cpuSpeeds.length);
+ for (LongSamplingCounter c : cpuSpeeds) {
+ if (c != null) {
+ out.writeInt(1);
+ c.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
+ }
+ } else {
+ out.writeInt(0);
+ }
}
+ } else {
+ out.writeInt(0);
}
}
@@ -5653,13 +5682,32 @@
mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase, in);
- int bins = in.readInt();
- int steps = getCpuSpeedSteps();
- mSpeedBins = new LongSamplingCounter[bins >= steps ? bins : steps];
- for (int i = 0; i < bins; i++) {
- if (in.readInt() != 0) {
- mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ if (in.readInt() != 0) {
+ int numCpuClusters = in.readInt();
+ if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numCpuClusters) {
+ throw new ParcelFormatException("Incompatible number of cpu clusters");
}
+
+ mCpuClusterSpeed = new LongSamplingCounter[numCpuClusters][];
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ if (in.readInt() != 0) {
+ int numSpeeds = in.readInt();
+ if (mPowerProfile != null &&
+ mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
+ throw new ParcelFormatException("Incompatible number of cpu speeds");
+ }
+
+ final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
+ mCpuClusterSpeed[cluster] = cpuSpeeds;
+ for (int speed = 0; speed < numSpeeds; speed++) {
+ if (in.readInt() != 0) {
+ cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ }
+ }
+ }
+ }
+ } else {
+ mCpuClusterSpeed = null;
}
}
@@ -6874,6 +6922,19 @@
public void setPowerProfile(PowerProfile profile) {
synchronized (this) {
mPowerProfile = profile;
+
+ // We need to initialize the KernelCpuSpeedReaders to read from
+ // the first cpu of each core. Once we have the PowerProfile, we have access to this
+ // information.
+ final int numClusters = mPowerProfile.getNumCpuClusters();
+ mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
+ int firstCpuOfCluster = 0;
+ for (int i = 0; i < numClusters; i++) {
+ final int numSpeedSteps = mPowerProfile.getNumSpeedStepsInCpuCluster(i);
+ mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
+ numSpeedSteps);
+ firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
+ }
}
}
@@ -6881,10 +6942,6 @@
mCallback = cb;
}
- public void setNumSpeedSteps(int steps) {
- if (sNumSpeedSteps == 0) sNumSpeedSteps = steps;
- }
-
public void setRadioScanningTimeout(long timeout) {
if (mPhoneSignalScanningTimer != null) {
mPhoneSignalScanningTimer.setTimeout(timeout);
@@ -7987,6 +8044,10 @@
* wakelocks. If the screen is on, we just assign the actual cpu time an app used.
*/
public void updateCpuTimeLocked() {
+ if (mPowerProfile == null) {
+ return;
+ }
+
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "!Cpu updating!");
}
@@ -7997,9 +8058,11 @@
// If no app is holding a wakelock, then the distribution is normal.
final int wakelockWeight = 50;
- // Read the time spent at various cpu frequencies.
- final int cpuSpeedSteps = getCpuSpeedSteps();
- final long[] cpuSpeeds = mKernelCpuSpeedReader.readDelta();
+ // Read the time spent for each cluster at various cpu frequencies.
+ final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][];
+ for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
+ clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
+ }
int numWakelocks = 0;
@@ -8072,11 +8135,28 @@
// Add the cpu speeds to this UID. These are used as a ratio
// for computing the power this UID used.
- for (int i = 0; i < cpuSpeedSteps; i++) {
- if (u.mSpeedBins[i] == null) {
- u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase);
+ final int numClusters = mPowerProfile.getNumCpuClusters();
+ if (u.mCpuClusterSpeed == null || u.mCpuClusterSpeed.length !=
+ numClusters) {
+ u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
+ }
+
+ for (int cluster = 0; cluster < clusterSpeeds.length; cluster++) {
+ final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(
+ cluster);
+ if (u.mCpuClusterSpeed[cluster] == null || speedsInCluster !=
+ u.mCpuClusterSpeed[cluster].length) {
+ u.mCpuClusterSpeed[cluster] =
+ new LongSamplingCounter[speedsInCluster];
}
- u.mSpeedBins[i].addCountLocked(cpuSpeeds[i]);
+
+ final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeed[cluster];
+ for (int speed = 0; speed < clusterSpeeds[cluster].length; speed++) {
+ if (cpuSpeeds[speed] == null) {
+ cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
+ }
+ cpuSpeeds[speed].addCountLocked(clusterSpeeds[cluster][speed]);
+ }
}
}
});
@@ -8776,11 +8856,6 @@
}
}
- @Override
- public int getCpuSpeedSteps() {
- return sNumSpeedSteps;
- }
-
/**
* Retrieve the statistics object for a particular uid, creating if needed.
*/
@@ -9216,11 +9291,6 @@
}
}
- sNumSpeedSteps = in.readInt();
- if (sNumSpeedSteps < 0 || sNumSpeedSteps > 100) {
- throw new ParcelFormatException("Bad speed steps in data: " + sNumSpeedSteps);
- }
-
final int NU = in.readInt();
if (NU > 10000) {
throw new ParcelFormatException("File corrupt: too many uids " + NU);
@@ -9304,17 +9374,33 @@
u.mSystemCpuTime.readSummaryFromParcelLocked(in);
u.mCpuPower.readSummaryFromParcelLocked(in);
- int NSB = in.readInt();
- if (NSB > 100) {
- throw new ParcelFormatException("File corrupt: too many speed bins " + NSB);
- }
-
- u.mSpeedBins = new LongSamplingCounter[NSB];
- for (int i=0; i<NSB; i++) {
- if (in.readInt() != 0) {
- u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase);
- u.mSpeedBins[i].readSummaryFromParcelLocked(in);
+ if (in.readInt() != 0) {
+ final int numClusters = in.readInt();
+ if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numClusters) {
+ throw new ParcelFormatException("Incompatible cpu cluster arrangement");
}
+
+ u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
+ for (int cluster = 0; cluster < numClusters; cluster++) {
+ int NSB = in.readInt();
+ if (mPowerProfile != null &&
+ mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) {
+ throw new ParcelFormatException("File corrupt: too many speed bins " + NSB);
+ }
+
+ if (in.readInt() != 0) {
+ u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB];
+ for (int speed = 0; speed < NSB; speed++) {
+ if (in.readInt() != 0) {
+ u.mCpuClusterSpeed[cluster][speed] = new LongSamplingCounter(
+ mOnBatteryTimeBase);
+ u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in);
+ }
+ }
+ }
+ }
+ } else {
+ u.mCpuClusterSpeed = null;
}
int NW = in.readInt();
@@ -9531,7 +9617,6 @@
}
}
- out.writeInt(sNumSpeedSteps);
final int NU = mUidStats.size();
out.writeInt(NU);
for (int iu = 0; iu < NU; iu++) {
@@ -9640,15 +9725,27 @@
u.mSystemCpuTime.writeSummaryFromParcelLocked(out);
u.mCpuPower.writeSummaryFromParcelLocked(out);
- out.writeInt(u.mSpeedBins.length);
- for (int i = 0; i < u.mSpeedBins.length; i++) {
- LongSamplingCounter speedBin = u.mSpeedBins[i];
- if (speedBin != null) {
- out.writeInt(1);
- speedBin.writeSummaryFromParcelLocked(out);
- } else {
- out.writeInt(0);
+ if (u.mCpuClusterSpeed != null) {
+ out.writeInt(1);
+ out.writeInt(u.mCpuClusterSpeed.length);
+ for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeed) {
+ if (cpuSpeeds != null) {
+ out.writeInt(1);
+ out.writeInt(cpuSpeeds.length);
+ for (LongSamplingCounter c : cpuSpeeds) {
+ if (c != null) {
+ out.writeInt(1);
+ c.writeSummaryFromParcelLocked(out);
+ } else {
+ out.writeInt(0);
+ }
+ }
+ } else {
+ out.writeInt(0);
+ }
}
+ } else {
+ out.writeInt(0);
}
final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap();
@@ -9897,8 +9994,6 @@
mFlashlightTurnedOnTimers.clear();
mCameraTurnedOnTimers.clear();
- sNumSpeedSteps = in.readInt();
-
int numUids = in.readInt();
mUidStats.clear();
for (int i = 0; i < numUids; i++) {
@@ -10037,8 +10132,6 @@
out.writeInt(0);
}
- out.writeInt(sNumSpeedSteps);
-
if (inclUids) {
int size = mUidStats.size();
out.writeInt(size);
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index d62f7a6..8417856 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -22,12 +22,47 @@
public class CpuPowerCalculator extends PowerCalculator {
private static final String TAG = "CpuPowerCalculator";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+ private final PowerProfile mProfile;
+
+ public CpuPowerCalculator(PowerProfile profile) {
+ mProfile = profile;
+ }
@Override
public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
long rawUptimeUs, int statsType) {
+
app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
- app.cpuPowerMah = (double) u.getCpuPowerMaUs(statsType) / (60.0 * 60.0 * 1000.0 * 1000.0);
+
+ // Aggregate total time spent on each cluster.
+ long totalTime = 0;
+ final int numClusters = mProfile.getNumCpuClusters();
+ for (int cluster = 0; cluster < numClusters; cluster++) {
+ final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
+ for (int speed = 0; speed < speedsForCluster; speed++) {
+ totalTime += u.getTimeAtCpuSpeed(cluster, speed, statsType);
+ }
+ }
+ totalTime = Math.max(totalTime, 1);
+
+ double cpuPowerMaMs = 0;
+ for (int cluster = 0; cluster < numClusters; cluster++) {
+ final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
+ for (int speed = 0; speed < speedsForCluster; speed++) {
+ final double ratio = (double) u.getTimeAtCpuSpeed(cluster, speed, statsType) /
+ totalTime;
+ final double cpuSpeedStepPower = ratio * app.cpuTimeMs *
+ mProfile.getAveragePowerForCpu(cluster, speed);
+ if (DEBUG && ratio != 0) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+ + speed + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
+ + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
+ }
+ cpuPowerMaMs += cpuSpeedStepPower;
+ }
+ }
+ app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);
+
if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
+ BatteryStatsHelper.makemAh(app.cpuPowerMah));
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index dcc6a5e..13d046e 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -20,6 +20,7 @@
import android.net.LocalSocketAddress;
import android.os.SystemClock;
import android.util.Slog;
+
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -91,31 +92,29 @@
}
}
- public int dexopt(String apkPath, int uid, boolean isPublic,
- String instructionSet, int dexoptNeeded) {
- return dexopt(apkPath, uid, isPublic, "*", instructionSet, dexoptNeeded,
- false, false, null);
+ public int dexopt(String apkPath, int uid, String instructionSet,
+ int dexoptNeeded, int dexFlags) {
+ return dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
+ null /*outputPath*/, dexFlags);
}
- public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
- String instructionSet, int dexoptNeeded, boolean vmSafeMode,
- boolean debuggable, String outputPath) {
+ public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+ int dexoptNeeded, String outputPath, int dexFlags) {
StringBuilder builder = new StringBuilder("dexopt");
builder.append(' ');
builder.append(apkPath);
builder.append(' ');
builder.append(uid);
- builder.append(isPublic ? " 1" : " 0");
builder.append(' ');
builder.append(pkgName);
builder.append(' ');
builder.append(instructionSet);
builder.append(' ');
builder.append(dexoptNeeded);
- builder.append(vmSafeMode ? " 1" : " 0");
- builder.append(debuggable ? " 1" : " 0");
builder.append(' ');
builder.append(outputPath != null ? outputPath : "!");
+ builder.append(' ');
+ builder.append(dexFlags);
return execute(builder.toString());
}
diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
index c30df28..5b776ac 100644
--- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java
+++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
@@ -24,8 +24,8 @@
import java.util.Arrays;
/**
- * Reads CPU time spent at various frequencies and provides a delta from the last call to
- * {@link #readDelta}. Each line in the proc file has the format:
+ * Reads CPU time of a specific core spent at various frequencies and provides a delta from the
+ * last call to {@link #readDelta}. Each line in the proc file has the format:
*
* freq time
*
@@ -33,12 +33,20 @@
*/
public class KernelCpuSpeedReader {
private static final String TAG = "KernelCpuSpeedReader";
- private static final String sProcFile =
- "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state";
- private static final int MAX_SPEEDS = 60;
- private long[] mLastSpeedTimes = new long[MAX_SPEEDS];
- private long[] mDeltaSpeedTimes = new long[MAX_SPEEDS];
+ private final String mProcFile;
+ private final long[] mLastSpeedTimes;
+ private final long[] mDeltaSpeedTimes;
+
+ /**
+ * @param cpuNumber The cpu (cpu0, cpu1, etc) whose state to read.
+ */
+ public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) {
+ mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
+ cpuNumber);
+ mLastSpeedTimes = new long[numSpeedSteps];
+ mDeltaSpeedTimes = new long[numSpeedSteps];
+ }
/**
* The returned array is modified in subsequent calls to {@link #readDelta}.
@@ -46,22 +54,28 @@
* {@link #readDelta}.
*/
public long[] readDelta() {
- try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
+ try (BufferedReader reader = new BufferedReader(new FileReader(mProcFile))) {
TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
String line;
int speedIndex = 0;
- while ((line = reader.readLine()) != null) {
+ while (speedIndex < mLastSpeedTimes.length && (line = reader.readLine()) != null) {
splitter.setString(line);
Long.parseLong(splitter.next());
// The proc file reports time in 1/100 sec, so convert to milliseconds.
long time = Long.parseLong(splitter.next()) * 10;
- mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex];
+ if (time < mLastSpeedTimes[speedIndex]) {
+ // The stats reset when the cpu hotplugged. That means that the time
+ // we read is offset from 0, so the time is the delta.
+ mDeltaSpeedTimes[speedIndex] = time;
+ } else {
+ mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex];
+ }
mLastSpeedTimes[speedIndex] = time;
speedIndex++;
}
} catch (IOException e) {
- Slog.e(TAG, "Failed to read cpu-freq", e);
+ Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage());
Arrays.fill(mDeltaSpeedTimes, 0);
}
return mDeltaSpeedTimes;
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
index 0df78ed..5d3043c 100644
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -137,7 +137,7 @@
mLastPowerMaUs.put(uid, powerMaUs);
}
} catch (IOException e) {
- Slog.e(TAG, "Failed to read uid_cputime", e);
+ Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
}
mLastTimeReadUs = nowUs;
}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 4ede8dda..aaa9f73 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -59,6 +59,7 @@
/**
* Power consumption when CPU is in power collapse mode.
*/
+ @Deprecated
public static final String POWER_CPU_ACTIVE = "cpu.active";
/**
@@ -163,6 +164,7 @@
*/
public static final String POWER_CAMERA = "camera.avg";
+ @Deprecated
public static final String POWER_CPU_SPEEDS = "cpu.speeds";
/**
@@ -191,6 +193,7 @@
if (sPowerMap.size() == 0) {
readPowerValuesFromXml(context);
}
+ initCpuClusters();
}
private void readPowerValuesFromXml(Context context) {
@@ -249,7 +252,7 @@
}
// Now collect other config variables.
- int[] configResIds = new int[] {
+ int[] configResIds = new int[]{
com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
@@ -260,7 +263,7 @@
com.android.internal.R.integer.config_wifi_operating_voltage_mv,
};
- String[] configResIdKeys = new String[] {
+ String[] configResIdKeys = new String[]{
POWER_BLUETOOTH_CONTROLLER_IDLE,
POWER_BLUETOOTH_CONTROLLER_RX,
POWER_BLUETOOTH_CONTROLLER_TX,
@@ -279,6 +282,69 @@
}
}
+ private CpuClusterKey[] mCpuClusters;
+
+ private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
+ private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster";
+ private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster";
+
+ @SuppressWarnings("deprecated")
+ private void initCpuClusters() {
+ // Figure out how many CPU clusters we're dealing with
+ final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT);
+ if (obj == null || !(obj instanceof Double[])) {
+ // Default to single.
+ mCpuClusters = new CpuClusterKey[1];
+ mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1);
+
+ } else {
+ final Double[] array = (Double[]) obj;
+ mCpuClusters = new CpuClusterKey[array.length];
+ for (int cluster = 0; cluster < array.length; cluster++) {
+ int numCpusInCluster = (int) Math.round(array[cluster]);
+ mCpuClusters[cluster] = new CpuClusterKey(
+ POWER_CPU_CLUSTER_SPEED_PREFIX + cluster,
+ POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster,
+ numCpusInCluster);
+ }
+ }
+ }
+
+ public static class CpuClusterKey {
+ private final String timeKey;
+ private final String powerKey;
+ private final int numCpus;
+
+ private CpuClusterKey(String timeKey, String powerKey, int numCpus) {
+ this.timeKey = timeKey;
+ this.powerKey = powerKey;
+ this.numCpus = numCpus;
+ }
+ }
+
+ public int getNumCpuClusters() {
+ return mCpuClusters.length;
+ }
+
+ public int getNumCoresInCpuCluster(int index) {
+ return mCpuClusters[index].numCpus;
+ }
+
+ public int getNumSpeedStepsInCpuCluster(int index) {
+ Object value = sPowerMap.get(mCpuClusters[index].timeKey);
+ if (value != null && value instanceof Double[]) {
+ return ((Double[])value).length;
+ }
+ return 1; // Only one speed
+ }
+
+ public double getAveragePowerForCpu(int cluster, int step) {
+ if (cluster >= 0 && cluster < mCpuClusters.length) {
+ return getAveragePower(mCpuClusters[cluster].powerKey, step);
+ }
+ return 0;
+ }
+
/**
* Returns the average current in mA consumed by the subsystem, or the given
* default value if the subsystem has no recorded value.
@@ -344,16 +410,4 @@
public double getBatteryCapacity() {
return getAveragePower(POWER_BATTERY_CAPACITY);
}
-
- /**
- * Returns the number of speeds that the CPU can be run at.
- * @return
- */
- public int getNumSpeedSteps() {
- Object value = sPowerMap.get(POWER_CPU_SPEEDS);
- if (value != null && value instanceof Double[]) {
- return ((Double[])value).length;
- }
- return 1; // Only one speed
- }
}
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 34ae58a..c558cf8 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -121,22 +121,4 @@
Zygote.appendQuotedShellArgs(command, args);
Zygote.execShell(command.toString());
}
-
- /**
- * Executes a standalone application with a wrapper command.
- * This method never returns.
- *
- * @param invokeWith The wrapper command.
- * @param classPath The class path.
- * @param className The class name to invoke.
- * @param args Arguments for the main() method of the specified class.
- */
- public static void execStandalone(String invokeWith, String classPath, String className,
- String[] args) {
- StringBuilder command = new StringBuilder(invokeWith);
- command.append(" /system/bin/dalvikvm -classpath '").append(classPath);
- command.append("' ").append(className);
- Zygote.appendQuotedShellArgs(command, args);
- Zygote.execShell(command.toString());
- }
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index af73097..aaa89df 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -486,8 +486,8 @@
final int dexoptNeeded = DexFile.getDexOptNeeded(
classPathElement, "*", instructionSet, false /* defer */);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
- instructionSet, dexoptNeeded);
+ installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
+ dexoptNeeded, 0 /*dexFlags*/);
}
}
} catch (IOException ioe) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 55e23b1..2e4d9b5 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -71,6 +71,7 @@
import com.android.internal.view.menu.ListMenuPresenter;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuDialogHelper;
+import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.MenuView;
import com.android.internal.widget.ActionBarContextView;
@@ -170,10 +171,15 @@
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
+ // When we reuse decor views, we need to recreate the content root. This happens when the decor
+ // view is requested, so we need to force the recreating without introducing an infinite loop.
+ private boolean mForceDecorInstall = false;
+
// This is the non client decor view for the window, containing the caption and window control
// buttons. The visibility of this decor depends on the workspace and the window type.
// If the window type does not require such a view, this member might be null.
NonClientDecorView mNonClientDecorView;
+
// The non client decor needs to adapt to the used workspace. Since querying and changing the
// workspace is expensive, this is the workspace value the window is currently set up for.
int mWorkspaceId;
@@ -247,6 +253,7 @@
private Drawable mBackgroundDrawable;
+ private boolean mLoadEleveation = true;
private float mElevation;
/** Whether window content should be clipped to the background outline. */
@@ -268,6 +275,7 @@
private ContextMenuBuilder mContextMenu;
private MenuDialogHelper mContextMenuHelper;
+ private MenuPopupHelper mContextMenuPopupHelper;
private boolean mClosingActionMenu;
private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
@@ -322,6 +330,16 @@
mLayoutInflater = LayoutInflater.from(context);
}
+ public PhoneWindow(Context context, Window preservedWindow) {
+ this(context);
+ if (preservedWindow != null) {
+ mDecor = (DecorView) preservedWindow.getDecorView();
+ mElevation = preservedWindow.getElevation();
+ mLoadEleveation = false;
+ mForceDecorInstall = true;
+ }
+ }
+
@Override
public final void setContainer(Window container) {
super.setContainer(container);
@@ -462,6 +480,12 @@
}
}
+ public void clearContentView() {
+ if (mNonClientDecorView.getChildCount() > 1) {
+ mNonClientDecorView.removeViewAt(1);
+ }
+ }
+
private void transitionTo(Scene scene) {
if (mContentScene == null) {
scene.enter();
@@ -1101,6 +1125,10 @@
mContextMenuHelper.dismiss();
mContextMenuHelper = null;
}
+ if (mContextMenuPopupHelper != null) {
+ mContextMenuPopupHelper.dismiss();
+ mContextMenuPopupHelper = null;
+ }
}
@Override
@@ -1395,6 +1423,11 @@
}
@Override
+ public float getElevation() {
+ return mElevation;
+ }
+
+ @Override
public final void setClipToOutline(boolean clipToOutline) {
mClipToOutline = clipToOutline;
if (mDecor != null) {
@@ -1991,7 +2024,7 @@
@Override
public final View getDecorView() {
- if (mDecor == null) {
+ if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
@@ -2495,6 +2528,10 @@
return onInterceptTouchEvent(event);
}
+ private boolean isOutOfInnerBounds(int x, int y) {
+ return x < 0 || y < 0 || x > getWidth() || y > getHeight();
+ }
+
private boolean isOutOfBounds(int x, int y) {
return x < -5 || y < -5 || x > (getWidth() + 5)
|| y > (getHeight() + 5);
@@ -2503,6 +2540,24 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
+ if (mHasNonClientDecor && mNonClientDecorView.mVisible) {
+ // Don't dispatch ACTION_DOWN to the non client decor if the window is
+ // resizable and the event was (starting) outside the window.
+ // Window resizing events should be handled by WindowManager.
+ // TODO: Investigate how to handle the outside touch in window manager
+ // without generating these events.
+ // Currently we receive these because we need to enlarge the window's
+ // touch region so that the monitor channel receives the events
+ // in the outside touch area.
+ if (action == MotionEvent.ACTION_DOWN) {
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ if (isOutOfInnerBounds(x, y)) {
+ return true;
+ }
+ }
+ }
+
if (mFeatureId >= 0) {
if (action == MotionEvent.ACTION_DOWN) {
int x = (int)event.getX();
@@ -2797,6 +2852,29 @@
}
@Override
+ public boolean showContextMenuForChild(View originalView, float x, float y) {
+ // Reuse the context menu builder
+ if (mContextMenu == null) {
+ mContextMenu = new ContextMenuBuilder(getContext());
+ mContextMenu.setCallback(mContextMenuCallback);
+ } else {
+ mContextMenu.clearAll();
+ }
+
+ final MenuPopupHelper helper = mContextMenu.showPopup(
+ getContext(), originalView, x, y);
+ if (helper != null) {
+ helper.setCallback(mContextMenuCallback);
+ } else if (mContextMenuPopupHelper != null) {
+ // No menu to show, but if we have a menu currently showing it just became blank.
+ // Close it.
+ mContextMenuPopupHelper.dismiss();
+ }
+ mContextMenuPopupHelper = helper;
+ return helper != null;
+ }
+
+ @Override
public ActionMode startActionModeForChild(View originalView,
ActionMode.Callback callback) {
return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
@@ -3937,7 +4015,9 @@
+ Integer.toHexString(mFrameResource));
}
}
- mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
+ if (mLoadEleveation) {
+ mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
+ }
mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
}
@@ -4009,8 +4089,10 @@
mNonClientDecorView = createNonClientDecorView();
View in = mLayoutInflater.inflate(layoutResource, null);
if (mNonClientDecorView != null) {
- decor.addView(mNonClientDecorView,
- new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ if (mNonClientDecorView.getParent() == null) {
+ decor.addView(mNonClientDecorView,
+ new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ }
mNonClientDecorView.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
@@ -4073,6 +4155,14 @@
// Free floating overlapping windows require a non client decor with a caption and shadow..
private NonClientDecorView createNonClientDecorView() {
NonClientDecorView nonClientDecorView = null;
+ for (int i = mDecor.getChildCount() - 1; i >= 0 && nonClientDecorView == null; i--) {
+ View view = mDecor.getChildAt(i);
+ if (view instanceof NonClientDecorView) {
+ // The decor was most likely saved from a relaunch - so reuse it.
+ nonClientDecorView = (NonClientDecorView) view;
+ mDecor.removeViewAt(i);
+ }
+ }
final WindowManager.LayoutParams attrs = getAttributes();
boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
attrs.type == TYPE_APPLICATION;
@@ -4083,21 +4173,22 @@
mWorkspaceId < FIRST_DYNAMIC_STACK_ID) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
- TypedValue value = new TypedValue();
- getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
- if (Color.luminance(value.data) < 0.5) {
- nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
- R.layout.non_client_decor_dark, null);
- } else {
- nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
- R.layout.non_client_decor_light, null);
+ if (nonClientDecorView == null) {
+ TypedValue value = new TypedValue();
+ getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+ if (Color.luminance(value.data) < 0.5) {
+ nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
+ R.layout.non_client_decor_dark, null);
+ } else {
+ nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
+ R.layout.non_client_decor_light, null);
+ }
}
nonClientDecorView.setPhoneWindow(this, hasNonClientDecor(mWorkspaceId),
nonClientDecorHasShadow(mWorkspaceId));
}
// Tell the decor if it has a visible non client decor.
- mDecor.enableNonClientDecor(nonClientDecorView != null &&
- hasNonClientDecor(mWorkspaceId));
+ mDecor.enableNonClientDecor(nonClientDecorView != null && hasNonClientDecor(mWorkspaceId));
return nonClientDecorView;
}
@@ -4108,6 +4199,7 @@
}
private void installDecor() {
+ mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
@@ -5284,4 +5376,9 @@
// TODO(skuhne): Add side by side mode here to add a decor.
return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
}
+
+ @Override
+ public boolean hasNonClientDecorView() {
+ return mNonClientDecorView != null;
+ }
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ab3ec98..11ef18b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -74,7 +74,9 @@
/**
* Notifies the status bar that a camera launch gesture has been detected.
+ *
+ * @param source the identifier for the gesture, see {@link StatusBarManager}
*/
- void onCameraLaunchGestureDetected();
+ void onCameraLaunchGestureDetected(int source);
}
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 44df0ce..b44baa2 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -35,7 +35,7 @@
public class FloatingActionMode extends ActionMode {
private static final int MAX_HIDE_DURATION = 3000;
- private static final int MOVING_HIDE_DELAY = 300;
+ private static final int MOVING_HIDE_DELAY = 50;
private final Context mContext;
private final ActionMode.Callback2 mCallback;
@@ -181,7 +181,6 @@
// Content rect is moving.
mOriginatingView.removeCallbacks(mMovingOff);
mFloatingToolbarVisibilityHelper.setMoving(true);
- mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY);
mFloatingToolbar.setContentRect(mContentRectOnScreen);
@@ -189,9 +188,9 @@
}
} else {
mFloatingToolbarVisibilityHelper.setOutOfBounds(true);
- mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
mContentRectOnScreen.setEmpty();
}
+ mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
mPreviousContentRectOnScreen.set(mContentRectOnScreen);
}
diff --git a/core/java/com/android/internal/view/IDropPermissionHolder.aidl b/core/java/com/android/internal/view/IDropPermissionHolder.aidl
new file mode 100644
index 0000000..e60ab0e
--- /dev/null
+++ b/core/java/com/android/internal/view/IDropPermissionHolder.aidl
@@ -0,0 +1,26 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.internal.view;
+
+/**
+ * Interface to allow a drop receiver to request permissions for URIs passed along with ClipData
+ * contained in DragEvent.
+ */
+interface IDropPermissionHolder {
+ void grant();
+ void revoke();
+}
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
new file mode 100644
index 0000000..293e2ad
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -0,0 +1,596 @@
+package com.android.internal.view.menu;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.View.OnKeyListener;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.DropDownListView;
+import android.widget.FrameLayout;
+import android.widget.MenuItemHoverListener;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.MenuPopupWindow;
+import android.widget.MenuPopupWindow.MenuDropDownListView;
+import android.widget.PopupWindow;
+import android.widget.PopupWindow.OnDismissListener;
+import android.widget.TextView;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A popup for a menu which will allow multiple submenus to appear in a cascading fashion, side by
+ * side.
+ * @hide
+ */
+final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKeyListener,
+ PopupWindow.OnDismissListener {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT})
+ public @interface HorizPosition {}
+
+ private static final int HORIZ_POSITION_LEFT = 0;
+ private static final int HORIZ_POSITION_RIGHT = 1;
+
+ private static final int SUBMENU_TIMEOUT_MS = 200;
+
+ private final Context mContext;
+ private final int mMenuMaxWidth;
+ private final int mPopupStyleAttr;
+ private final int mPopupStyleRes;
+ private final boolean mOverflowOnly;
+ private final int mLayoutDirection;
+ private final Handler mSubMenuHoverHandler;
+
+ private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (isShowing()) {
+ final View anchor = mShownAnchorView;
+ if (anchor == null || !anchor.isShown()) {
+ dismiss();
+ } else if (isShowing()) {
+ // Recompute window sizes and positions.
+ for (MenuPopupWindow popup : mPopupWindows) {
+ popup.show();
+ }
+ }
+ }
+ }
+ };
+
+ private final OnAttachStateChangeListener mAttachStateChangeListener =
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) {
+ mTreeObserver = v.getViewTreeObserver();
+ }
+ mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ }
+ v.removeOnAttachStateChangeListener(this);
+ }
+ };
+
+ private final MenuItemHoverListener mMenuItemHoverListener = new MenuItemHoverListener() {
+ @Override
+ public void onItemHovered(MenuBuilder menu, int position) {
+ int menuIndex = -1;
+ for (int i = 0; i < mListViews.size(); i++) {
+ final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(i);
+ final MenuAdapter adapter = toMenuAdapter(view.getAdapter());
+
+ if (adapter.getAdapterMenu() == menu) {
+ menuIndex = i;
+ break;
+ }
+ }
+
+ if (menuIndex == -1) {
+ return;
+ }
+
+ final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(menuIndex);
+ final ListMenuItemView selectedItemView = (ListMenuItemView) view.getSelectedView();
+
+ if (selectedItemView != null && selectedItemView.isEnabled()
+ && selectedItemView.getItemData().hasSubMenu()) {
+ // If the currently selected item corresponds to a submenu, schedule to open the
+ // submenu on a timeout.
+
+ mSubMenuHoverHandler.removeCallbacksAndMessages(null);
+ mSubMenuHoverHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ // Make sure the submenu item is still the one selected.
+ if (view.getSelectedView() == selectedItemView
+ && selectedItemView.isEnabled()
+ && selectedItemView.getItemData().hasSubMenu()) {
+ // Close any other submenus that might be open at the current or
+ // a deeper level.
+ int nextIndex = mListViews.indexOf(view) + 1;
+ if (nextIndex < mListViews.size()) {
+ MenuAdapter nextSubMenuAdapter =
+ toMenuAdapter(mListViews.get(nextIndex).getAdapter());
+ // Disable exit animation, to prevent overlapping fading out
+ // submenus.
+ mPopupWindows.get(nextIndex).setExitTransition(null);
+ nextSubMenuAdapter.getAdapterMenu().close();
+ }
+
+ // Then open the selected submenu.
+ view.performItemClick(
+ selectedItemView,
+ view.getSelectedItemPosition(),
+ view.getSelectedItemId());
+ }
+ }
+ }, SUBMENU_TIMEOUT_MS);
+ } else if (menuIndex + 1 < mListViews.size()) {
+ // If the currently selected item does NOT corresponds to a submenu, check if there
+ // is a submenu already open that is one level deeper. If so, schedule to close it
+ // on a timeout.
+
+ final MenuDropDownListView nextView =
+ (MenuDropDownListView) mListViews.get(menuIndex + 1);
+ final MenuAdapter nextAdapter = toMenuAdapter(nextView.getAdapter());
+
+ mSubMenuHoverHandler.removeCallbacksAndMessages(null);
+ mSubMenuHoverHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ // Make sure the menu wasn't already closed by something else and that
+ // it wasn't re-hovered by the user since this was scheduled.
+ int nextMenuIndex = mListViews.indexOf(nextView);
+
+ if (nextMenuIndex != -1 && nextView.getSelectedView() == null) {
+ // Disable exit animation, to prevent overlapping fading out submenus.
+ for (int i = nextMenuIndex; i < mPopupWindows.size(); i++) {
+ final MenuPopupWindow popupWindow = mPopupWindows.get(i);
+ popupWindow.setExitTransition(null);
+ popupWindow.setAnimationStyle(0);
+ }
+ nextAdapter.getAdapterMenu().close();
+ }
+ }
+ }, SUBMENU_TIMEOUT_MS);
+ }
+ }
+ };
+
+ private int mDropDownGravity = Gravity.NO_GRAVITY;
+ private View mAnchorView;
+ private View mShownAnchorView;
+ private List<DropDownListView> mListViews;
+ private List<MenuPopupWindow> mPopupWindows;
+ private int mLastPosition;
+ private List<Integer> mPositions;
+ private List<int[]> mOffsets;
+ private int mInitXOffset;
+ private int mInitYOffset;
+ private boolean mForceShowIcon;
+ private boolean mShowTitle;
+ private Callback mPresenterCallback;
+ private ViewTreeObserver mTreeObserver;
+ private PopupWindow.OnDismissListener mOnDismissListener;
+
+ /**
+ * Initializes a new cascading-capable menu popup.
+ *
+ * @param parent A parent view to get the {@link android.view.View#getWindowToken()} token from.
+ */
+ public CascadingMenuPopup(Context context, View anchor, int popupStyleAttr,
+ int popupStyleRes, boolean overflowOnly) {
+ mContext = Preconditions.checkNotNull(context);
+ mAnchorView = Preconditions.checkNotNull(anchor);
+ mPopupStyleAttr = popupStyleAttr;
+ mPopupStyleRes = popupStyleRes;
+ mOverflowOnly = overflowOnly;
+
+ mForceShowIcon = false;
+
+ final Resources res = context.getResources();
+ final Configuration config = res.getConfiguration();
+ mLayoutDirection = config.getLayoutDirection();
+ mLastPosition = getInitialMenuPosition();
+ mMenuMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
+ res.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
+
+ mPopupWindows = new ArrayList<MenuPopupWindow>();
+ mListViews = new ArrayList<DropDownListView>();
+ mOffsets = new ArrayList<int[]>();
+ mPositions = new ArrayList<Integer>();
+ mSubMenuHoverHandler = new Handler();
+ }
+
+ @Override
+ public void setForceShowIcon(boolean forceShow) {
+ mForceShowIcon = forceShow;
+ }
+
+ private MenuPopupWindow createPopupWindow() {
+ MenuPopupWindow popupWindow = new MenuPopupWindow(
+ mContext, null, mPopupStyleAttr, mPopupStyleRes);
+ popupWindow.setHoverListener(mMenuItemHoverListener);
+ popupWindow.setOnItemClickListener(this);
+ popupWindow.setOnDismissListener(this);
+ popupWindow.setAnchorView(mAnchorView);
+ popupWindow.setDropDownGravity(mDropDownGravity);
+ popupWindow.setModal(true);
+ return popupWindow;
+ }
+
+ @Override
+ public void show() {
+ if (isShowing()) {
+ return;
+ }
+
+ // Show any menus that have been added via #addMenu(MenuBuilder) but which have not yet been
+ // shown.
+ // In a typical use case, #addMenu(MenuBuilder) would be called once, followed by a call to
+ // this #show() method -- which would actually show the popup on the screen.
+ for (int i = 0; i < mPopupWindows.size(); i++) {
+ MenuPopupWindow popupWindow = mPopupWindows.get(i);
+ popupWindow.show();
+ DropDownListView listView = (DropDownListView) popupWindow.getListView();
+ mListViews.add(listView);
+
+ MenuBuilder menu = toMenuAdapter(listView.getAdapter()).getAdapterMenu();
+ if (i == 0 && mShowTitle && menu.getHeaderTitle() != null) {
+ FrameLayout titleItemView =
+ (FrameLayout) LayoutInflater.from(mContext).inflate(
+ com.android.internal.R.layout.popup_menu_header_item_layout,
+ listView,
+ false);
+ TextView titleView = (TextView) titleItemView.findViewById(
+ com.android.internal.R.id.title);
+ titleView.setText(menu.getHeaderTitle());
+ titleItemView.setEnabled(false);
+ listView.addHeaderView(titleItemView, null, false);
+
+ // Update to show the title.
+ popupWindow.show();
+ }
+ }
+
+ mShownAnchorView = mAnchorView;
+ if (mShownAnchorView != null) {
+ final boolean addGlobalListener = mTreeObserver == null;
+ mTreeObserver = mShownAnchorView.getViewTreeObserver(); // Refresh to latest
+ if (addGlobalListener) {
+ mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
+ }
+ mShownAnchorView.addOnAttachStateChangeListener(mAttachStateChangeListener);
+ }
+ }
+
+ @Override
+ public void dismiss() {
+ // Need to make another list to avoid a concurrent modification exception, as #onDismiss
+ // may clear mPopupWindows while we are iterating.
+ List<MenuPopupWindow> popupWindows = new ArrayList<MenuPopupWindow>(mPopupWindows);
+ for (MenuPopupWindow popupWindow : popupWindows) {
+ if (popupWindow != null && popupWindow.isShowing()) {
+ popupWindow.dismiss();
+ }
+ }
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
+ dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determines the proper initial menu position for the current LTR/RTL configuration.
+ * @return The initial position.
+ */
+ @HorizPosition
+ private int getInitialMenuPosition() {
+ return mLayoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT :
+ HORIZ_POSITION_RIGHT;
+ }
+
+ /**
+ * Determines whether the next submenu (of the given width) should display on the right or on
+ * the left of the most recent menu.
+ *
+ * @param nextMenuWidth Width of the next submenu to display.
+ * @return The position to display it.
+ */
+ @HorizPosition
+ private int getNextMenuPosition(int nextMenuWidth) {
+ ListView lastListView = mListViews.get(mListViews.size() - 1);
+
+ final int[] screenLocation = new int[2];
+ lastListView.getLocationOnScreen(screenLocation);
+
+ final Rect displayFrame = new Rect();
+ mShownAnchorView.getWindowVisibleDisplayFrame(displayFrame);
+
+ if (mLastPosition == HORIZ_POSITION_RIGHT) {
+ final int right = screenLocation[0] + lastListView.getWidth() + nextMenuWidth;
+ if (right > displayFrame.right) {
+ return HORIZ_POSITION_LEFT;
+ }
+ return HORIZ_POSITION_RIGHT;
+ } else { // LEFT
+ final int left = screenLocation[0] - nextMenuWidth;
+ if (left < 0) {
+ return HORIZ_POSITION_RIGHT;
+ }
+ return HORIZ_POSITION_LEFT;
+ }
+ }
+
+ @Override
+ public void addMenu(MenuBuilder menu) {
+ boolean addSubMenu = mListViews.size() > 0;
+
+ menu.addMenuPresenter(this, mContext);
+
+ MenuPopupWindow popupWindow = createPopupWindow();
+
+ MenuAdapter adapter = new MenuAdapter(menu, LayoutInflater.from(mContext), mOverflowOnly);
+ adapter.setForceShowIcon(mForceShowIcon);
+
+ popupWindow.setAdapter(adapter);
+
+ int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
+
+ int x = 0;
+ int y = 0;
+
+ if (addSubMenu) {
+ popupWindow.setTouchModal(false);
+ popupWindow.setEnterTransition(null);
+
+ ListView lastListView = mListViews.get(mListViews.size() - 1);
+ @HorizPosition int nextMenuPosition = getNextMenuPosition(menuWidth);
+ boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
+ mLastPosition = nextMenuPosition;
+
+ int[] lastLocation = new int[2];
+ lastListView.getLocationOnScreen(lastLocation);
+
+ int[] lastOffset = mOffsets.get(mOffsets.size() - 1);
+
+ // Note: By now, mDropDownGravity is the absolute gravity, so this should work in both
+ // LTR and RTL.
+ if ((mDropDownGravity & Gravity.RIGHT) == Gravity.RIGHT) {
+ if (showOnRight) {
+ x = lastOffset[0] + menuWidth;
+ } else {
+ x = lastOffset[0] - lastListView.getWidth();
+ }
+ } else {
+ if (showOnRight) {
+ x = lastOffset[0] + lastListView.getWidth();
+ } else {
+ x = lastOffset[0] - menuWidth;
+ }
+ }
+
+ y = lastOffset[1] + lastListView.getSelectedView().getTop() -
+ lastListView.getChildAt(0).getTop();
+ } else {
+ x = mInitXOffset;
+ y = mInitYOffset;
+ }
+
+ popupWindow.setWidth(menuWidth);
+ popupWindow.setHorizontalOffset(x);
+ popupWindow.setVerticalOffset(y);
+ mPopupWindows.add(popupWindow);
+
+ // NOTE: This case handles showing submenus once the CascadingMenuPopup has already
+ // been shown via a call to its #show() method. If it hasn't yet been show()n, then
+ // we deliberately do not yet show the popupWindow, as #show() will do that later.
+ if (isShowing()) {
+ popupWindow.show();
+ DropDownListView listView = (DropDownListView) popupWindow.getListView();
+ mListViews.add(listView);
+ }
+
+ int[] offsets = {x, y};
+ mOffsets.add(offsets);
+ mPositions.add(mLastPosition);
+ }
+
+ /**
+ * @return {@code true} if the popup is currently showing, {@code false} otherwise.
+ */
+ @Override
+ public boolean isShowing() {
+ return mPopupWindows.size() > 0 && mPopupWindows.get(0).isShowing();
+ }
+
+ /**
+ * Called when one or more of the popup windows was dismissed.
+ */
+ @Override
+ public void onDismiss() {
+ int dismissedIndex = -1;
+ for (int i = 0; i < mPopupWindows.size(); i++) {
+ if (!mPopupWindows.get(i).isShowing()) {
+ dismissedIndex = i;
+ break;
+ }
+ }
+
+ if (dismissedIndex != -1) {
+ for (int i = dismissedIndex; i < mListViews.size(); i++) {
+ ListView view = mListViews.get(i);
+ ListAdapter adapter = view.getAdapter();
+ MenuAdapter menuAdapter = toMenuAdapter(adapter);
+ menuAdapter.mAdapterMenu.close();
+ }
+ }
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ for (ListView view : mListViews) {
+ toMenuAdapter(view.getAdapter()).notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void setCallback(Callback cb) {
+ mPresenterCallback = cb;
+ }
+
+ @Override
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ // Don't allow double-opening of the same submenu.
+ for (ListView view : mListViews) {
+ if (toMenuAdapter(view.getAdapter()).mAdapterMenu.equals(subMenu)) {
+ // Just re-focus that one.
+ view.requestFocus();
+ return true;
+ }
+ }
+
+ if (subMenu.hasVisibleItems()) {
+ this.addMenu(subMenu);
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onOpenSubMenu(subMenu);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ int menuIndex = -1;
+ boolean wasSelected = false;
+
+ for (int i = 0; i < mListViews.size(); i++) {
+ ListView view = mListViews.get(i);
+ MenuAdapter adapter = toMenuAdapter(view.getAdapter());
+
+ if (menuIndex == -1 && menu == adapter.mAdapterMenu) {
+ menuIndex = i;
+ wasSelected = view.getSelectedView() != null;
+ }
+
+ // Once the menu has been found, remove it and all submenus beneath it from the
+ // container view. Also remove the presenter.
+ if (menuIndex != -1) {
+ adapter.mAdapterMenu.removeMenuPresenter(this);
+ }
+ }
+
+ // Then, actually remove the views for these [sub]menu(s) from our list of views.
+ if (menuIndex != -1) {
+ for (int i = menuIndex; i < mPopupWindows.size(); i++) {
+ mPopupWindows.get(i).dismiss();
+ }
+ mPopupWindows.subList(menuIndex, mPopupWindows.size()).clear();
+ mListViews.subList(menuIndex, mListViews.size()).clear();
+ mOffsets.subList(menuIndex, mOffsets.size()).clear();
+
+ mPositions.subList(menuIndex, mPositions.size()).clear();
+ if (mPositions.size() > 0) {
+ mLastPosition = mPositions.get(mPositions.size() - 1);
+ } else {
+ mLastPosition = getInitialMenuPosition();
+ }
+ }
+
+ if (mListViews.size() == 0 || wasSelected) {
+ dismiss();
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ if (mPopupWindows.size() == 0) {
+ if (mTreeObserver != null) {
+ if (mTreeObserver.isAlive()) {
+ mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ }
+ mTreeObserver = null;
+ }
+ mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
+ // If every [sub]menu was dismissed, that means the whole thing was dismissed, so notify
+ // the owner.
+ mOnDismissListener.onDismiss();
+ }
+ }
+
+ @Override
+ public boolean flagActionItems() {
+ return false;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
+
+ @Override
+ public void setGravity(int dropDownGravity) {
+ mDropDownGravity = Gravity.getAbsoluteGravity(dropDownGravity, mLayoutDirection);
+ }
+
+ @Override
+ public void setAnchorView(View anchor) {
+ mAnchorView = anchor;
+ }
+
+ @Override
+ public void setOnDismissListener(OnDismissListener listener) {
+ mOnDismissListener = listener;
+ }
+
+ @Override
+ public ListView getListView() {
+ return mListViews.size() > 0 ? mListViews.get(mListViews.size() - 1) : null;
+ }
+
+ @Override
+ public void setHorizontalOffset(int x) {
+ mInitXOffset = x;
+ }
+
+ @Override
+ public void setVerticalOffset(int y) {
+ mInitYOffset = y;
+ }
+
+ @Override
+ public void setShowTitle(boolean showTitle) {
+ mShowTitle = showTitle;
+ }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
index bf44d51..aaa1bf1 100644
--- a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
@@ -17,6 +17,7 @@
package com.android.internal.view.menu;
import android.content.Context;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
import android.util.EventLog;
@@ -93,4 +94,29 @@
return null;
}
+ public MenuPopupHelper showPopup(Context context, View originalView, float x, float y) {
+ if (originalView != null) {
+ // Let relevant views and their populate context listeners populate
+ // the context menu
+ originalView.createContextMenu(this);
+ }
+
+ if (getVisibleItems().size() > 0) {
+ EventLog.writeEvent(50001, 1);
+
+ int location[] = new int[2];
+ originalView.getLocationOnScreen(location);
+
+ final MenuPopupHelper helper = new MenuPopupHelper(
+ context,
+ this,
+ originalView,
+ false /* overflowOnly */,
+ com.android.internal.R.attr.contextPopupMenuStyle);
+ helper.show(Math.round(x), Math.round(y));
+ return helper;
+ }
+
+ return null;
+ }
}
diff --git a/core/java/com/android/internal/view/menu/MenuAdapter.java b/core/java/com/android/internal/view/menu/MenuAdapter.java
index 1e03b1f..673cfd1 100644
--- a/core/java/com/android/internal/view/menu/MenuAdapter.java
+++ b/core/java/com/android/internal/view/menu/MenuAdapter.java
@@ -40,6 +40,10 @@
findExpandedIndex();
}
+ public boolean getForceShowIcon() {
+ return mForceShowIcon;
+ }
+
public void setForceShowIcon(boolean forceShow) {
mForceShowIcon = forceShow;
}
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index e8d1ead..1673928 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -64,6 +64,7 @@
private final Context mContext;
private final Resources mResources;
+ private final boolean mShowCascadingMenus;
/**
* Whether the shortcuts should be qwerty-accessible. Use isQwertyMode()
@@ -186,6 +187,8 @@
public MenuBuilder(Context context) {
mContext = context;
mResources = context.getResources();
+ mShowCascadingMenus = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableCascadingSubmenus);
mItems = new ArrayList<MenuItemImpl>();
@@ -909,7 +912,9 @@
invoked |= itemImpl.expandActionView();
if (invoked) close(true);
} else if (itemImpl.hasSubMenu() || providerHasSubMenu) {
- close(false);
+ if (!mShowCascadingMenus) {
+ close(false);
+ }
if (!itemImpl.hasSubMenu()) {
itemImpl.setSubMenu(new SubMenuBuilder(getContext(), this, itemImpl));
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 08d4e86..624c9e2 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -149,7 +149,7 @@
return true;
}
- if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) {
+ if (mMenu.dispatchMenuItemSelected(mMenu, this)) {
return true;
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java
index 91788ca..98f5d90 100644
--- a/core/java/com/android/internal/view/menu/MenuPopup.java
+++ b/core/java/com/android/internal/view/menu/MenuPopup.java
@@ -17,10 +17,13 @@
package com.android.internal.view.menu;
import android.content.Context;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
+import android.widget.AdapterView;
import android.widget.FrameLayout;
+import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.PopupWindow;
@@ -30,14 +33,17 @@
*
* @hide
*/
-public abstract class MenuPopup implements ShowableListMenu, MenuPresenter {
+public abstract class MenuPopup implements ShowableListMenu, MenuPresenter,
+ AdapterView.OnItemClickListener {
public abstract void setForceShowIcon(boolean forceShow);
/**
- * Adds the given menu to the popup. If this is the first menu shown it'll be displayed; if it's
- * a submenu it will be displayed adjacent to the most recent menu (if supported by the
- * implementation).
+ * Adds the given menu to the popup, if it is capable of displaying submenus within itself.
+ * If menu is the first menu shown, it won't be displayed until show() is called.
+ * If the popup was already showing, adding a submenu via this method will cause that new
+ * submenu to be shown immediately (that is, if this MenuPopup implementation is capable of
+ * showing its own submenus).
*
* @param menu
*/
@@ -47,6 +53,18 @@
public abstract void setAnchorView(View anchor);
+ public abstract void setHorizontalOffset(int x);
+
+ public abstract void setVerticalOffset(int y);
+
+ /**
+ * Set whether a title entry should be shown in the popup menu (if a title exists for the
+ * menu).
+ *
+ * @param showTitle
+ */
+ public abstract void setShowTitle(boolean showTitle);
+
/**
* Set a listener to receive a callback when the popup is dismissed.
*
@@ -79,6 +97,16 @@
return 0;
}
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ListAdapter outerAdapter = (ListAdapter) parent.getAdapter();
+ MenuAdapter wrappedAdapter = toMenuAdapter(outerAdapter);
+
+ // Use the position from the outer adapter so that if a header view was added, we don't get
+ // an off-by-1 error in position.
+ wrappedAdapter.mAdapterMenu.performItemAction((MenuItem) outerAdapter.getItem(position), 0);
+ }
+
/**
* Measures the width of the given menu view.
*
@@ -119,4 +147,19 @@
return maxWidth;
}
-}
\ No newline at end of file
+
+ /**
+ * Converts the given ListAdapter originating from a menu, to a MenuAdapter, accounting for
+ * the possibility of the parameter adapter actually wrapping the MenuAdapter. (That could
+ * happen if a header view was added on the menu.)
+ *
+ * @param adapter
+ * @return
+ */
+ protected static MenuAdapter toMenuAdapter(ListAdapter adapter) {
+ if (adapter instanceof HeaderViewListAdapter) {
+ return (MenuAdapter) ((HeaderViewListAdapter) adapter).getWrappedAdapter();
+ }
+ return (MenuAdapter) adapter;
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 9a47fc1..e674ecc 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -16,12 +16,11 @@
package com.android.internal.view.menu;
+import com.android.internal.view.menu.MenuPresenter.Callback;
+
import android.content.Context;
-import android.os.Parcelable;
import android.view.Gravity;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.widget.PopupWindow;
/**
@@ -29,8 +28,7 @@
*
* @hide
*/
-public class MenuPopupHelper implements ViewTreeObserver.OnGlobalLayoutListener,
- PopupWindow.OnDismissListener, View.OnAttachStateChangeListener, MenuPresenter {
+public class MenuPopupHelper implements PopupWindow.OnDismissListener {
private final Context mContext;
private final MenuBuilder mMenu;
private final boolean mOverflowOnly;
@@ -39,9 +37,13 @@
private View mAnchorView;
private MenuPopup mPopup;
- private ViewTreeObserver mTreeObserver;
private int mDropDownGravity = Gravity.NO_GRAVITY;
+ private boolean mForceShowIcon;
+ private boolean mShowTitle;
+ private Callback mPresenterCallback;
+ private int mInitXOffset;
+ private int mInitYOffset;
public MenuPopupHelper(Context context, MenuBuilder menu) {
this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0);
@@ -70,9 +72,8 @@
private MenuPopup createMenuPopup() {
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableCascadingSubmenus)) {
- // TODO: Return a Cascading implementation of MenuPopup instead.
- return new StandardMenuPopup(
- mContext, mMenu, mAnchorView, mPopupStyleAttr, mPopupStyleRes, mOverflowOnly);
+ return new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr, mPopupStyleRes,
+ mOverflowOnly);
}
return new StandardMenuPopup(
mContext, mMenu, mAnchorView, mPopupStyleAttr, mPopupStyleRes, mOverflowOnly);
@@ -84,6 +85,7 @@
}
public void setForceShowIcon(boolean forceShow) {
+ mForceShowIcon = forceShow;
mPopup.setForceShowIcon(forceShow);
}
@@ -102,6 +104,12 @@
}
}
+ public void show(int x, int y) {
+ if (!tryShow(x, y)) {
+ throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
+ }
+ }
+
public ShowableListMenu getPopup() {
return mPopup;
}
@@ -117,18 +125,45 @@
return true;
}
- final View anchor = mAnchorView;
- if (anchor != null) {
- final boolean addGlobalListener = mTreeObserver == null;
- mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
- if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
- anchor.addOnAttachStateChangeListener(this);
- mPopup.setAnchorView(anchor);
- mPopup.setGravity(mDropDownGravity);
- } else {
+ if (mAnchorView == null) {
return false;
}
+ mInitXOffset = 0;
+ mInitYOffset = 0;
+ mShowTitle = false;
+
+ showPopup();
+ return true;
+ }
+
+ public boolean tryShow(int x, int y) {
+ if (isShowing()) {
+ return true;
+ }
+
+ if (mAnchorView == null) {
+ return false;
+ }
+
+ mInitXOffset = x;
+ mInitYOffset = y;
+ mShowTitle = true;
+
+ showPopup();
+ return true;
+ }
+
+ private void showPopup() {
+ mPopup = createMenuPopup();
+ mPopup.setAnchorView(mAnchorView);
+ mPopup.setCallback(mPresenterCallback);
+ mPopup.setForceShowIcon(mForceShowIcon);
+ mPopup.setGravity(mDropDownGravity);
+ mPopup.setHorizontalOffset(mInitXOffset);
+ mPopup.setShowTitle(mShowTitle);
+ mPopup.setVerticalOffset(mInitYOffset);
+
// In order for subclasses of MenuPopupHelper to satisfy the OnDismissedListener interface,
// we must set the listener to this outer Helper rather than to the inner MenuPopup.
// Not to worry -- the inner MenuPopup will call our own #onDismiss method after it's done
@@ -137,7 +172,6 @@
mPopup.addMenu(mMenu);
mPopup.show();
- return true;
}
public void dismiss() {
@@ -149,100 +183,14 @@
@Override
public void onDismiss() {
mPopup = null;
- if (mTreeObserver != null) {
- if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
- mTreeObserver.removeGlobalOnLayoutListener(this);
- mTreeObserver = null;
- }
- mAnchorView.removeOnAttachStateChangeListener(this);
}
public boolean isShowing() {
return mPopup != null && mPopup.isShowing();
}
- @Override
- public void onGlobalLayout() {
- if (isShowing()) {
- final View anchor = mAnchorView;
- if (anchor == null || !anchor.isShown()) {
- dismiss();
- } else if (isShowing()) {
- // Recompute window size and position
- mPopup.show();
- }
- }
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- if (mTreeObserver != null) {
- if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
- mTreeObserver.removeGlobalOnLayoutListener(this);
- }
- v.removeOnAttachStateChangeListener(this);
- }
-
- @Override
- public void initForMenu(Context context, MenuBuilder menu) {
- // Don't need to do anything; we added as a presenter in the constructor.
- }
-
- @Override
- public MenuView getMenuView(ViewGroup root) {
- throw new UnsupportedOperationException("MenuPopupHelpers manage their own views");
- }
-
- @Override
- public void updateMenuView(boolean cleared) {
- mPopup.updateMenuView(cleared);
- }
-
- @Override
- public void setCallback(Callback cb) {
+ public void setCallback(MenuPresenter.Callback cb) {
+ mPresenterCallback = cb;
mPopup.setCallback(cb);
}
-
- @Override
- public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
- return mPopup.onSubMenuSelected(subMenu);
- }
-
- @Override
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- mPopup.onCloseMenu(menu, allMenusAreClosing);
- }
-
- @Override
- public boolean flagActionItems() {
- return false;
- }
-
- @Override
- public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
- return false;
- }
-
- @Override
- public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
- return false;
- }
-
- @Override
- public int getId() {
- return 0;
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- return null;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- }
}
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index 9a30ffa..caee0d2 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -6,14 +6,17 @@
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnKeyListener;
-import android.widget.AdapterView;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.MenuPopupWindow;
import android.widget.PopupWindow;
+import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.PopupWindow.OnDismissListener;
@@ -34,15 +37,53 @@
private final int mPopupMaxWidth;
private final int mPopupStyleAttr;
private final int mPopupStyleRes;
+ // The popup window is final in order to couple its lifecycle to the lifecycle of the
+ // StandardMenuPopup.
+ private final MenuPopupWindow mPopup;
+
+ private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (isShowing()) {
+ final View anchor = mShownAnchorView;
+ if (anchor == null || !anchor.isShown()) {
+ dismiss();
+ } else if (isShowing()) {
+ // Recompute window size and position
+ mPopup.show();
+ }
+ }
+ }
+ };
+
+ private final OnAttachStateChangeListener mAttachStateChangeListener =
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ }
+ v.removeOnAttachStateChangeListener(this);
+ }
+ };
private PopupWindow.OnDismissListener mOnDismissListener;
private View mAnchorView;
- private MenuPopupWindow mPopup;
+ private View mShownAnchorView;
private Callback mPresenterCallback;
+ private ViewTreeObserver mTreeObserver;
private ViewGroup mMeasureParent;
+ /** Whether the popup has been dismissed. Once dismissed, it cannot be opened again. */
+ private boolean mWasDismissed;
+
/** Whether the cached content width value is valid. */
private boolean mHasContentWidth;
@@ -51,6 +92,10 @@
private int mDropDownGravity = Gravity.NO_GRAVITY;
+ private int mXOffset;
+ private int mYOffset;
+ private boolean mShowTitle;
+
public StandardMenuPopup(Context context, MenuBuilder menu, View anchorView, int popupStyleAttr,
int popupStyleRes, boolean overflowOnly) {
mContext = Preconditions.checkNotNull(context);
@@ -88,18 +133,26 @@
return true;
}
+ if (mWasDismissed || mAnchorView == null) {
+ return false;
+ }
+
+ mShownAnchorView = mAnchorView;
+
mPopup.setOnDismissListener(this);
mPopup.setOnItemClickListener(this);
mPopup.setAdapter(mAdapter);
mPopup.setModal(true);
- final View anchor = mAnchorView;
- if (anchor != null) {
- mPopup.setAnchorView(anchor);
- mPopup.setDropDownGravity(mDropDownGravity);
- } else {
- return false;
+ final View anchor = mShownAnchorView;
+ final boolean addGlobalListener = mTreeObserver == null;
+ mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
+ if (addGlobalListener) {
+ mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
}
+ anchor.addOnAttachStateChangeListener(mAttachStateChangeListener);
+ mPopup.setAnchorView(anchor);
+ mPopup.setDropDownGravity(mDropDownGravity);
if (!mHasContentWidth) {
mContentWidth = measureIndividualMenuWidth(
@@ -109,8 +162,28 @@
mPopup.setContentWidth(mContentWidth);
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ mPopup.setHorizontalOffset(mXOffset);
+ mPopup.setVerticalOffset(mYOffset);
mPopup.show();
- mPopup.getListView().setOnKeyListener(this);
+
+ ListView listView = mPopup.getListView();
+ listView.setOnKeyListener(this);
+
+ if (mShowTitle && mMenu.getHeaderTitle() != null) {
+ FrameLayout titleItemView =
+ (FrameLayout) LayoutInflater.from(mContext).inflate(
+ com.android.internal.R.layout.popup_menu_header_item_layout,
+ listView,
+ false);
+ TextView titleView = (TextView) titleItemView.findViewById(
+ com.android.internal.R.id.title);
+ titleView.setText(mMenu.getHeaderTitle());
+ titleItemView.setEnabled(false);
+ listView.addHeaderView(titleItemView, null, false);
+
+ // Update to show the title.
+ mPopup.show();
+ }
return true;
}
@@ -129,26 +202,26 @@
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- MenuAdapter adapter = mAdapter;
- adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
- }
-
- @Override
public void addMenu(MenuBuilder menu) {
// No-op: standard implementation has only one menu which is set in the constructor.
}
@Override
public boolean isShowing() {
- return mPopup != null && mPopup.isShowing();
+ return !mWasDismissed && mPopup.isShowing();
}
@Override
public void onDismiss() {
- mPopup = null;
+ mWasDismissed = true;
mMenu.close();
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = mShownAnchorView.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ mTreeObserver = null;
+ }
+ mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
mOnDismissListener.onDismiss();
}
@@ -170,19 +243,10 @@
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
if (subMenu.hasVisibleItems()) {
MenuPopupHelper subPopup = new MenuPopupHelper(
- mContext, subMenu, mAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
+ mContext, subMenu, mShownAnchorView, mOverflowOnly, mPopupStyleAttr,
+ mPopupStyleRes);
subPopup.setCallback(mPresenterCallback);
-
- boolean preserveIconSpacing = false;
- final int count = subMenu.size();
- for (int i = 0; i < count; i++) {
- MenuItem childItem = subMenu.getItem(i);
- if (childItem.isVisible() && childItem.getIcon() != null) {
- preserveIconSpacing = true;
- break;
- }
- }
- subPopup.setForceShowIcon(preserveIconSpacing);
+ subPopup.setForceShowIcon(mAdapter.getForceShowIcon());
if (subPopup.tryShow()) {
if (mPresenterCallback != null) {
@@ -210,7 +274,6 @@
return false;
}
-
@Override
public Parcelable onSaveInstanceState() {
return null;
@@ -243,4 +306,20 @@
public ListView getListView() {
return mPopup.getListView();
}
-}
\ No newline at end of file
+
+
+ @Override
+ public void setHorizontalOffset(int x) {
+ mXOffset = x;
+ }
+
+ @Override
+ public void setVerticalOffset(int y) {
+ mYOffset = y;
+ }
+
+ @Override
+ public void setShowTitle(boolean showTitle) {
+ mShowTitle = showTitle;
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
index 92acf8c..cf741bf 100644
--- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
@@ -73,7 +73,7 @@
@Override
public MenuBuilder getRootMenu() {
- return mParentMenu;
+ return mParentMenu.getRootMenu();
}
@Override
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index ca6fe61..7bab446 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -73,6 +73,8 @@
// This class is responsible for the public API of the floating toolbar.
// It delegates rendering operations to the FloatingToolbarPopup.
+ public static final String FLOATING_TOOLBAR_TAG = "floating_toolbar";
+
private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
new MenuItem.OnMenuItemClickListener() {
@Override
@@ -1460,8 +1462,10 @@
}
private static ViewGroup createContentContainer(Context context) {
- return (ViewGroup) LayoutInflater.from(context)
+ ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
.inflate(R.layout.floating_popup_container, null);
+ contentContainer.setTag(FLOATING_TOOLBAR_TAG);
+ return contentContainer;
}
private static PopupWindow createPopupWindow(View content) {
@@ -1488,10 +1492,9 @@
private static AnimatorSet createEnterAnimation(View view) {
AnimatorSet animation = new AnimatorSet();
animation.playTogether(
- ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(200),
+ ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150),
// Make sure that view.x is always fixed throughout the duration of this animation.
ObjectAnimator.ofFloat(view, View.X, view.getX(), view.getX()));
- animation.setStartDelay(50);
return animation;
}
@@ -1506,7 +1509,7 @@
View view, int startDelay, Animator.AnimatorListener listener) {
AnimatorSet animation = new AnimatorSet();
animation.playTogether(
- ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(200));
+ ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100));
animation.setStartDelay(startDelay);
animation.addListener(listener);
return animation;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d96a909..60ef4a4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1075,12 +1075,22 @@
* enter a pattern.
*/
public long getLockoutAttemptDeadline(int userId) {
- final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
+ long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId);
final long now = SystemClock.elapsedRealtime();
- if (deadline < now || deadline > (now + timeoutMs)) {
+ if (deadline < now && deadline != 0) {
+ // timeout expired
+ setLong(LOCKOUT_ATTEMPT_DEADLINE, 0, userId);
+ setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0, userId);
return 0L;
}
+
+ if (deadline > (now + timeoutMs)) {
+ // device was rebooted, set new deadline
+ deadline = now + timeoutMs;
+ setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId);
+ }
+
return deadline;
}
@@ -1178,7 +1188,7 @@
* @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
*/
public void requireCredentialEntry(int userId) {
- requireStrongAuth(StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
+ requireStrongAuth(StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
}
/**
@@ -1260,7 +1270,7 @@
value = { STRONG_AUTH_NOT_REQUIRED,
STRONG_AUTH_REQUIRED_AFTER_BOOT,
STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW,
- STRONG_AUTH_REQUIRED_AFTER_USER_REQUEST})
+ SOME_AUTH_REQUIRED_AFTER_USER_REQUEST})
@Retention(RetentionPolicy.SOURCE)
public @interface StrongAuthFlags {}
@@ -1275,14 +1285,14 @@
public static final int STRONG_AUTH_REQUIRED_AFTER_BOOT = 0x1;
/**
- * Strong authentication is required because a device admin has temporarily requested it.
+ * Strong authentication is required because a device admin has requested it.
*/
public static final int STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW = 0x2;
/**
- * Strong authentication is required because the user has temporarily requested it.
+ * Some authentication is required because the user has temporarily disabled trust.
*/
- public static final int STRONG_AUTH_REQUIRED_AFTER_USER_REQUEST = 0x4;
+ public static final int SOME_AUTH_REQUIRED_AFTER_USER_REQUEST = 0x4;
/**
* Strong authentication is required because the user has been locked out after too many
@@ -1298,6 +1308,7 @@
public static final int DEFAULT = STRONG_AUTH_REQUIRED_AFTER_BOOT;
private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED
+ | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST
| SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL;
private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 491b323..6ab306c 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -26,7 +26,6 @@
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.Window;
-import android.view.WindowInsets;
import android.util.Log;
import android.util.TypedValue;
@@ -71,16 +70,11 @@
// True if the window is being dragged.
private boolean mDragging = false;
- // The bounds of the window and the absolute mouse pointer coordinates from before we started to
- // drag the window. They will be used to determine the next window position.
- private final Rect mWindowOriginalBounds = new Rect();
- private float mStartDragX;
- private float mStartDragY;
// True when the left mouse button got released while dragging.
private boolean mLeftMouseButtonReleased;
- // Avoiding re-creation of Rect's by keeping a temporary window drag bound.
- private final Rect mWindowDragBounds = new Rect();
+ // True if this window is resizable (which is currently only true when the decor is shown).
+ public boolean mVisible = false;
// The current focus state of the window for updating the window elevation.
private boolean mWindowHasFocus = true;
@@ -128,18 +122,14 @@
// When there is no decor we should not react to anything.
return false;
}
- // Ensure that the activity is active.
- activateActivity();
// A drag action is started if we aren't dragging already and the starting event is
// either a left mouse button or any other input device.
if (!mDragging &&
(e.getToolType(e.getActionIndex()) != MotionEvent.TOOL_TYPE_MOUSE ||
(e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0)) {
mDragging = true;
- mWindowOriginalBounds.set(getActivityBounds());
mLeftMouseButtonReleased = false;
- mStartDragX = e.getRawX();
- mStartDragY = e.getRawY();
+ startMovingTask(e.getRawX(), e.getRawY());
}
break;
@@ -152,29 +142,17 @@
mLeftMouseButtonReleased = true;
break;
}
- mWindowDragBounds.set(mWindowOriginalBounds);
- mWindowDragBounds.offset(Math.round(e.getRawX() - mStartDragX),
- Math.round(e.getRawY() - mStartDragY));
- setActivityBounds(mWindowDragBounds);
}
break;
case MotionEvent.ACTION_UP:
- if (mDragging) {
- // Since the window is already where it should be we don't have to do anything
- // special at this time.
- mDragging = false;
- return true;
- }
- break;
-
case MotionEvent.ACTION_CANCEL:
- if (mDragging) {
- mDragging = false;
- setActivityBounds(mWindowOriginalBounds);
- return true;
+ if (!mDragging) {
+ break;
}
- break;
+ // Abort the ongoing dragging.
+ mDragging = false;
+ return true;
}
return mDragging;
}
@@ -246,6 +224,7 @@
boolean invisible = isFillingScreen() || !mShowDecor;
View caption = getChildAt(0);
caption.setVisibility(invisible ? GONE : VISIBLE);
+ mVisible = !invisible;
}
/**
@@ -312,54 +291,4 @@
}
}
}
-
- /**
- * Returns the bounds of this activity.
- * @return Returns bounds of the activity. It will return null if either the window is
- * fullscreen or the bounds could not be retrieved.
- */
- private Rect getActivityBounds() {
- Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
- if (callback != null) {
- try {
- return callback.getActivityBounds();
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to get the activity bounds.");
- }
- }
- return null;
- }
-
- /**
- * Sets the bounds of this Activity on the stack.
- * @param newBounds The bounds of the activity. Passing null is not allowed.
- */
- private void setActivityBounds(Rect newBounds) {
- if (newBounds == null) {
- Log.e(TAG, "Failed to set null bounds to the activity.");
- return;
- }
- Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
- if (callback != null) {
- try {
- callback.setActivityBounds(newBounds);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to set the activity bounds.");
- }
- }
- }
-
- /**
- * Activates the activity - means setting the focus and moving it to the top of the stack.
- */
- private void activateActivity() {
- Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
- if (callback != null) {
- try {
- callback.activateActivity();
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to activate the activity.");
- }
- }
- }
}
diff --git a/core/java/com/android/server/LocalServices.java b/core/java/com/android/server/LocalServices.java
index 25dcb30..9c632ea 100644
--- a/core/java/com/android/server/LocalServices.java
+++ b/core/java/com/android/server/LocalServices.java
@@ -16,6 +16,8 @@
package com.android.server;
+import com.android.internal.annotations.VisibleForTesting;
+
import android.util.ArrayMap;
/**
@@ -57,4 +59,14 @@
sLocalServiceObjects.put(type, service);
}
}
+
+ /**
+ * Remove a service instance, must be only used in tests.
+ */
+ @VisibleForTesting
+ public static <T> void removeServiceForTest(Class<T> type) {
+ synchronized (sLocalServiceObjects) {
+ sLocalServiceObjects.remove(type);
+ }
+ }
}
diff --git a/core/java/com/android/server/backup/NotificationBackupHelper.java b/core/java/com/android/server/backup/NotificationBackupHelper.java
index 6f5c7e8..0d225e8 100644
--- a/core/java/com/android/server/backup/NotificationBackupHelper.java
+++ b/core/java/com/android/server/backup/NotificationBackupHelper.java
@@ -46,7 +46,8 @@
try {
INotificationManager nm = INotificationManager.Stub.asInterface(
ServiceManager.getService("notification"));
- newPayload = nm.getBackupPayload(UserHandle.USER_OWNER);
+ // TODO: http://b/22388012
+ newPayload = nm.getBackupPayload(UserHandle.USER_SYSTEM);
} catch (Exception e) {
// Treat as no data
Slog.e(TAG, "Couldn't communicate with notification manager");
@@ -66,7 +67,8 @@
try {
INotificationManager nm = INotificationManager.Stub.asInterface(
ServiceManager.getService("notification"));
- nm.applyRestore(payload, UserHandle.USER_OWNER);
+ // TODO: http://b/22388012
+ nm.applyRestore(payload, UserHandle.USER_SYSTEM);
} catch (Exception e) {
Slog.e(TAG, "Couldn't communicate with notification manager");
}
diff --git a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
index 458a2ca..8063670 100644
--- a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
+++ b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
@@ -52,13 +52,14 @@
Slog.d(TAG, "Handling backup of " + key);
}
try {
+ // TODO: http://b/22388012
switch (key) {
case KEY_PREFERRED:
- return pm.getPreferredActivityBackup(UserHandle.USER_OWNER);
+ return pm.getPreferredActivityBackup(UserHandle.USER_SYSTEM);
case KEY_DEFAULT_APPS:
- return pm.getDefaultAppsBackup(UserHandle.USER_OWNER);
+ return pm.getDefaultAppsBackup(UserHandle.USER_SYSTEM);
case KEY_INTENT_VERIFICATION:
- return pm.getIntentFilterVerificationBackup(UserHandle.USER_OWNER);
+ return pm.getIntentFilterVerificationBackup(UserHandle.USER_SYSTEM);
default:
Slog.w(TAG, "Unexpected backup key " + key);
}
@@ -75,15 +76,16 @@
Slog.d(TAG, "Handling restore of " + key);
}
try {
+ // TODO: http://b/22388012
switch (key) {
case KEY_PREFERRED:
- pm.restorePreferredActivities(payload, UserHandle.USER_OWNER);
+ pm.restorePreferredActivities(payload, UserHandle.USER_SYSTEM);
break;
case KEY_DEFAULT_APPS:
- pm.restoreDefaultApps(payload, UserHandle.USER_OWNER);
+ pm.restoreDefaultApps(payload, UserHandle.USER_SYSTEM);
break;
case KEY_INTENT_VERIFICATION:
- pm.restoreIntentFilterVerification(payload, UserHandle.USER_OWNER);
+ pm.restoreIntentFilterVerification(payload, UserHandle.USER_SYSTEM);
break;
default:
Slog.w(TAG, "Unexpected restore key " + key);
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index f485460..234815f 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -54,13 +54,15 @@
private static final String WALLPAPER_INFO_FILENAME = "wallpaper_info.xml";
// TODO: Will need to change if backing up non-primary user's wallpaper
+ // TODO: http://b/22388012
private static final String WALLPAPER_IMAGE_DIR =
- Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath();
+ Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM).getAbsolutePath();
private static final String WALLPAPER_IMAGE = WallpaperBackupHelper.WALLPAPER_IMAGE;
// TODO: Will need to change if backing up non-primary user's wallpaper
+ // TODO: http://b/22388012
private static final String WALLPAPER_INFO_DIR =
- Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath();
+ Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM).getAbsolutePath();
private static final String WALLPAPER_INFO = WallpaperBackupHelper.WALLPAPER_INFO;
// Use old keys to keep legacy data compatibility and avoid writing two wallpapers
private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 75da27e..20d00b0 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -1,6 +1,5 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += -DHAVE_CONFIG_H -DKHTML_NO_EXCEPTIONS -DGKWQ_NO_JAVA
LOCAL_CFLAGS += -DNO_SUPPORT_JS_BINDING -DQT_NO_WHEELEVENT -DKHTML_NO_XBL
@@ -259,6 +258,9 @@
# <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp
LOCAL_C_INCLUDES += bionic/libc/private
+# AndroidRuntime.h depends on nativehelper/jni.h
+LOCAL_EXPORT_C_INCLUDE_DIRS := libnativehelper/include
+
LOCAL_MODULE:= libandroid_runtime
# -Wno-unknown-pragmas: necessary for Clang as the GL bindings need to turn
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e4bc800..ffc69a9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -217,7 +217,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nativeFinishInit", "()V",
(void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
{ "nativeZygoteInit", "()V",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index fbe3ece..e6c7c2b 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1348,7 +1348,7 @@
///////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gBitmapMethods[] = {
+static const JNINativeMethod gBitmapMethods[] = {
{ "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;",
(void*)Bitmap_creator },
{ "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;",
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 20a54e5..28bc7fe 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -542,7 +542,7 @@
///////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nativeDecodeStream",
"(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
(void*)nativeDecodeStream
@@ -569,7 +569,7 @@
},
};
-static JNINativeMethod gOptionsMethods[] = {
+static const JNINativeMethod gOptionsMethods[] = {
{ "requestCancel", "()V", (void*)nativeRequestCancel }
};
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 8535e6a..2df3a46 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -47,7 +47,7 @@
fHeight = height;
}
~SkBitmapRegionDecoder() {
- SkDELETE(fDecoder);
+ delete fDecoder;
}
bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
@@ -72,7 +72,7 @@
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
int width, height;
if (NULL == decoder) {
- SkDELETE(stream);
+ delete stream;
doThrowIOE(env, "Image format not supported");
return nullObjectReturn("SkImageDecoder::Factory returned null");
}
@@ -87,7 +87,7 @@
snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
decoder->getFormatName());
doThrowIOE(env, msg);
- SkDELETE(decoder);
+ delete decoder;
return nullObjectReturn("decoder->buildTileIndex returned false");
}
@@ -261,7 +261,7 @@
///////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gBitmapRegionDecoderMethods[] = {
+static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
{ "nativeDecodeRegion",
"(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
(void*)nativeDecodeRegion},
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index 036ece1..6fcf689 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -115,7 +115,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gCameraMethods[] = {
+static const JNINativeMethod gCameraMethods[] = {
/* name, signature, funcPtr */
{ "nativeConstructor", "()V", (void*)Camera_constructor },
diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/core/jni/android/graphics/CanvasProperty.cpp
index deb4971..728bc1c 100644
--- a/core/jni/android/graphics/CanvasProperty.cpp
+++ b/core/jni/android/graphics/CanvasProperty.cpp
@@ -39,7 +39,7 @@
// JNI Glue
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nCreateFloat", "(F)J", (void*) createFloat },
{ "nCreatePaint", "(J)J", (void*) createPaint },
};
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index d03bcf0..83fd073 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -57,19 +57,19 @@
}
};
-static JNINativeMethod colorfilter_methods[] = {
+static const JNINativeMethod colorfilter_methods[] = {
{"destroyFilter", "(J)V", (void*) SkColorFilterGlue::finalizer}
};
-static JNINativeMethod porterduff_methods[] = {
+static const JNINativeMethod porterduff_methods[] = {
{ "native_CreatePorterDuffFilter", "(II)J", (void*) SkColorFilterGlue::CreatePorterDuffFilter },
};
-static JNINativeMethod lighting_methods[] = {
+static const JNINativeMethod lighting_methods[] = {
{ "native_CreateLightingFilter", "(II)J", (void*) SkColorFilterGlue::CreateLightingFilter },
};
-static JNINativeMethod colormatrix_methods[] = {
+static const JNINativeMethod colormatrix_methods[] = {
{ "nativeColorMatrixFilter", "([F)J", (void*) SkColorFilterGlue::CreateColorMatrixFilter },
};
diff --git a/core/jni/android/graphics/DrawFilter.cpp b/core/jni/android/graphics/DrawFilter.cpp
index 90ef6c0..c1dc0dd 100644
--- a/core/jni/android/graphics/DrawFilter.cpp
+++ b/core/jni/android/graphics/DrawFilter.cpp
@@ -97,11 +97,11 @@
}
};
-static JNINativeMethod drawfilter_methods[] = {
+static const JNINativeMethod drawfilter_methods[] = {
{"nativeDestructor", "(J)V", (void*) SkDrawFilterGlue::finalizer}
};
-static JNINativeMethod paintflags_methods[] = {
+static const JNINativeMethod paintflags_methods[] = {
{"nativeConstructor","(II)J", (void*) SkDrawFilterGlue::CreatePaintFlagsDF}
};
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 38db76b..dac6d96 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -124,7 +124,7 @@
///////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gFontFamilyMethods[] = {
+static const JNINativeMethod gFontFamilyMethods[] = {
{ "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/lang/String;)Z", (void*)FontFamily_addFont },
diff --git a/core/jni/android/graphics/Interpolator.cpp b/core/jni/android/graphics/Interpolator.cpp
index 3593d1a..fa28359 100644
--- a/core/jni/android/graphics/Interpolator.cpp
+++ b/core/jni/android/graphics/Interpolator.cpp
@@ -71,7 +71,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gInterpolatorMethods[] = {
+static const JNINativeMethod gInterpolatorMethods[] = {
{ "nativeConstructor", "(II)J", (void*)Interpolator_constructor },
{ "nativeDestructor", "(J)V", (void*)Interpolator_destructor },
{ "nativeReset", "(JII)V", (void*)Interpolator_reset },
diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp
index d658643..2b4a1ab 100644
--- a/core/jni/android/graphics/MaskFilter.cpp
+++ b/core/jni/android/graphics/MaskFilter.cpp
@@ -61,19 +61,19 @@
}
};
-static JNINativeMethod gMaskFilterMethods[] = {
+static const JNINativeMethod gMaskFilterMethods[] = {
{ "nativeDestructor", "(J)V", (void*)SkMaskFilterGlue::destructor }
};
-static JNINativeMethod gBlurMaskFilterMethods[] = {
+static const JNINativeMethod gBlurMaskFilterMethods[] = {
{ "nativeConstructor", "(FI)J", (void*)SkMaskFilterGlue::createBlur }
};
-static JNINativeMethod gEmbossMaskFilterMethods[] = {
+static const JNINativeMethod gEmbossMaskFilterMethods[] = {
{ "nativeConstructor", "([FFFF)J", (void*)SkMaskFilterGlue::createEmboss }
};
-static JNINativeMethod gTableMaskFilterMethods[] = {
+static const JNINativeMethod gTableMaskFilterMethods[] = {
{ "nativeNewTable", "([B)J", (void*)SkMaskFilterGlue::createTable },
{ "nativeNewClip", "(II)J", (void*)SkMaskFilterGlue::createClipTable },
{ "nativeNewGamma", "(F)J", (void*)SkMaskFilterGlue::createGammaTable }
diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp
index 101e2ba..b0f3bb4 100644
--- a/core/jni/android/graphics/Matrix.cpp
+++ b/core/jni/android/graphics/Matrix.cpp
@@ -302,7 +302,7 @@
}
};
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"finalizer", "(J)V", (void*) SkMatrixGlue::finalizer},
{"native_create","(J)J", (void*) SkMatrixGlue::create},
diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp
index d67ed10..498c590 100644
--- a/core/jni/android/graphics/Movie.cpp
+++ b/core/jni/android/graphics/Movie.cpp
@@ -135,7 +135,7 @@
//////////////////////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "width", "()I", (void*)movie_width },
{ "height", "()I", (void*)movie_height },
{ "isOpaque", "()Z", (void*)movie_isOpaque },
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 1c28262..3ccbb35 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -108,7 +108,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gNinePatchMethods[] = {
+static const JNINativeMethod gNinePatchMethods[] = {
{ "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk },
{ "validateNinePatchChunk", "([B)J",
(void*) SkNinePatchGlue::validateNinePatchChunk },
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 988d13a..520dc4f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -1095,7 +1095,7 @@
};
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"finalizer", "(J)V", (void*) PaintGlue::finalizer},
{"native_init","()J", (void*) PaintGlue::init},
{"native_initWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index dbd7c89..2998c99 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -475,7 +475,7 @@
}
};
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"finalizer", "(J)V", (void*) SkPathGlue::finalizer},
{"init1","()J", (void*) SkPathGlue::init1},
{"init2","(J)J", (void*) SkPathGlue::init2},
diff --git a/core/jni/android/graphics/PathEffect.cpp b/core/jni/android/graphics/PathEffect.cpp
index 265944e..b289b21 100644
--- a/core/jni/android/graphics/PathEffect.cpp
+++ b/core/jni/android/graphics/PathEffect.cpp
@@ -69,31 +69,31 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gPathEffectMethods[] = {
+static const JNINativeMethod gPathEffectMethods[] = {
{ "nativeDestructor", "(J)V", (void*)SkPathEffectGlue::destructor }
};
-static JNINativeMethod gComposePathEffectMethods[] = {
+static const JNINativeMethod gComposePathEffectMethods[] = {
{ "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Compose_constructor }
};
-static JNINativeMethod gSumPathEffectMethods[] = {
+static const JNINativeMethod gSumPathEffectMethods[] = {
{ "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Sum_constructor }
};
-static JNINativeMethod gDashPathEffectMethods[] = {
+static const JNINativeMethod gDashPathEffectMethods[] = {
{ "nativeCreate", "([FF)J", (void*)SkPathEffectGlue::Dash_constructor }
};
-static JNINativeMethod gPathDashPathEffectMethods[] = {
+static const JNINativeMethod gPathDashPathEffectMethods[] = {
{ "nativeCreate", "(JFFI)J", (void*)SkPathEffectGlue::OneD_constructor }
};
-static JNINativeMethod gCornerPathEffectMethods[] = {
+static const JNINativeMethod gCornerPathEffectMethods[] = {
{ "nativeCreate", "(F)J", (void*)SkPathEffectGlue::Corner_constructor }
};
-static JNINativeMethod gDiscretePathEffectMethods[] = {
+static const JNINativeMethod gDiscretePathEffectMethods[] = {
{ "nativeCreate", "(FF)J", (void*)SkPathEffectGlue::Discrete_constructor }
};
diff --git a/core/jni/android/graphics/PathMeasure.cpp b/core/jni/android/graphics/PathMeasure.cpp
index fec5d9d..70e528d 100644
--- a/core/jni/android/graphics/PathMeasure.cpp
+++ b/core/jni/android/graphics/PathMeasure.cpp
@@ -143,7 +143,7 @@
}
};
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"native_create", "(JZ)J", (void*) SkPathMeasureGlue::create },
{"native_setPath", "(JJZ)V", (void*) SkPathMeasureGlue::setPath },
{"native_getLength", "(J)F", (void*) SkPathMeasureGlue::getLength },
diff --git a/core/jni/android/graphics/PorterDuff.cpp b/core/jni/android/graphics/PorterDuff.cpp
index fed90a5..dfc3c9d 100644
--- a/core/jni/android/graphics/PorterDuff.cpp
+++ b/core/jni/android/graphics/PorterDuff.cpp
@@ -58,7 +58,7 @@
};
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"nativeCreateXfermode","(I)J", (void*) SkPorterDuffGlue::CreateXfermode},
};
diff --git a/core/jni/android/graphics/Rasterizer.cpp b/core/jni/android/graphics/Rasterizer.cpp
index cfc23ac8..a106ecf 100644
--- a/core/jni/android/graphics/Rasterizer.cpp
+++ b/core/jni/android/graphics/Rasterizer.cpp
@@ -61,7 +61,7 @@
}
};
-static JNINativeMethod gRasterizerMethods[] = {
+static const JNINativeMethod gRasterizerMethods[] = {
{"finalizer", "(J)V", (void*) SkRasterizerGlue::finalizer}
};
@@ -85,7 +85,7 @@
}
};
-static JNINativeMethod gLayerRasterizerMethods[] = {
+static const JNINativeMethod gLayerRasterizerMethods[] = {
{ "nativeConstructor", "()J", (void*)SkLayerRasterizerGlue::create },
{ "nativeAddLayer", "(JJFF)V", (void*)SkLayerRasterizerGlue::addLayer }
};
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index e99bdfc..bcd0b60 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -306,13 +306,13 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gRegionIterMethods[] = {
+static const JNINativeMethod gRegionIterMethods[] = {
{ "nativeConstructor", "(J)J", (void*)RegionIter_constructor },
{ "nativeDestructor", "(J)V", (void*)RegionIter_destructor },
{ "nativeNext", "(JLandroid/graphics/Rect;)Z", (void*)RegionIter_next }
};
-static JNINativeMethod gRegionMethods[] = {
+static const JNINativeMethod gRegionMethods[] = {
// these are static methods
{ "nativeConstructor", "()J", (void*)Region_constructor },
{ "nativeDestructor", "(J)V", (void*)Region_destructor },
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 49c377e..799ed83 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -240,36 +240,36 @@
///////////////////////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gColorMethods[] = {
+static const JNINativeMethod gColorMethods[] = {
{ "nativeRGBToHSV", "(III[F)V", (void*)Color_RGBToHSV },
{ "nativeHSVToColor", "(I[F)I", (void*)Color_HSVToColor }
};
-static JNINativeMethod gShaderMethods[] = {
+static const JNINativeMethod gShaderMethods[] = {
{ "nativeDestructor", "(J)V", (void*)Shader_destructor },
{ "nativeSetLocalMatrix", "(JJ)J", (void*)Shader_setLocalMatrix }
};
-static JNINativeMethod gBitmapShaderMethods[] = {
+static const JNINativeMethod gBitmapShaderMethods[] = {
{ "nativeCreate", "(Landroid/graphics/Bitmap;II)J", (void*)BitmapShader_constructor },
};
-static JNINativeMethod gLinearGradientMethods[] = {
+static const JNINativeMethod gLinearGradientMethods[] = {
{ "nativeCreate1", "(FFFF[I[FI)J", (void*)LinearGradient_create1 },
{ "nativeCreate2", "(FFFFIII)J", (void*)LinearGradient_create2 },
};
-static JNINativeMethod gRadialGradientMethods[] = {
+static const JNINativeMethod gRadialGradientMethods[] = {
{ "nativeCreate1", "(FFF[I[FI)J", (void*)RadialGradient_create1 },
{ "nativeCreate2", "(FFFIII)J", (void*)RadialGradient_create2 },
};
-static JNINativeMethod gSweepGradientMethods[] = {
+static const JNINativeMethod gSweepGradientMethods[] = {
{ "nativeCreate1", "(FF[I[F)J", (void*)SweepGradient_create1 },
{ "nativeCreate2", "(FFII)J", (void*)SweepGradient_create2 },
};
-static JNINativeMethod gComposeShaderMethods[] = {
+static const JNINativeMethod gComposeShaderMethods[] = {
{ "nativeCreate1", "(JJJ)J", (void*)ComposeShader_create1 },
{ "nativeCreate2", "(JJI)J", (void*)ComposeShader_create2 },
};
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index b9e48a0..61dc6e4 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -241,17 +241,16 @@
BufferQueue::createBufferQueue(&producer, &consumer);
if (singleBufferMode) {
- consumer->disableAsyncBuffer();
- consumer->setDefaultMaxBufferCount(1);
+ consumer->setMaxBufferCount(1);
}
sp<GLConsumer> surfaceTexture;
if (isDetached) {
surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES,
- true, true);
+ true, !singleBufferMode);
} else {
surfaceTexture = new GLConsumer(consumer, texName,
- GL_TEXTURE_EXTERNAL_OES, true, true);
+ GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
}
if (surfaceTexture == 0) {
@@ -360,7 +359,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gSurfaceTextureMethods[] = {
+static const JNINativeMethod gSurfaceTextureMethods[] = {
{"nativeClassInit", "()V", (void*)SurfaceTexture_classInit },
{"nativeInit", "(ZIZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init },
{"nativeFinalize", "()V", (void*)SurfaceTexture_finalize },
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index e0cbc9e..e97b768d 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -68,7 +68,7 @@
///////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gTypefaceMethods[] = {
+static const JNINativeMethod gTypefaceMethods[] = {
{ "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface },
{ "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias },
{ "nativeUnref", "(J)V", (void*)Typeface_unref },
diff --git a/core/jni/android/graphics/Xfermode.cpp b/core/jni/android/graphics/Xfermode.cpp
index 4a424ae..7441acc 100644
--- a/core/jni/android/graphics/Xfermode.cpp
+++ b/core/jni/android/graphics/Xfermode.cpp
@@ -47,15 +47,15 @@
///////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gXfermodeMethods[] = {
+static const JNINativeMethod gXfermodeMethods[] = {
{"finalizer", "(J)V", (void*) SkXfermodeGlue::finalizer}
};
-static JNINativeMethod gAvoidMethods[] = {
+static const JNINativeMethod gAvoidMethods[] = {
{"nativeCreate", "(III)J", (void*) SkXfermodeGlue::avoid_create}
};
-static JNINativeMethod gPixelXorMethods[] = {
+static const JNINativeMethod gPixelXorMethods[] = {
{"nativeCreate", "(I)J", (void*) SkXfermodeGlue::pixelxor_create}
};
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
index 5eede2a..7d0c39c 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.cpp
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -243,7 +243,7 @@
}
///////////////////////////////////////////////////////////////////////////////
-static JNINativeMethod gYuvImageMethods[] = {
+static const JNINativeMethod gYuvImageMethods[] = {
{ "nativeCompressToJpeg", "([BIII[I[IILjava/io/OutputStream;[B)Z",
(void*)YuvImage_compressToJpeg }
};
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index a91b15b..7a13fe4 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -150,7 +150,7 @@
document->close();
}
-static JNINativeMethod gPdfDocument_Methods[] = {
+static const JNINativeMethod gPdfDocument_Methods[] = {
{"nativeCreateDocument", "()J", (void*) nativeCreateDocument},
{"nativeStartPage", "(JIIIIII)J", (void*) nativeStartPage},
{"nativeFinishPage", "(J)V", (void*) nativeFinishPage},
diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp
index 52b69e0..0177635 100644
--- a/core/jni/android/graphics/pdf/PdfEditor.cpp
+++ b/core/jni/android/graphics/pdf/PdfEditor.cpp
@@ -343,7 +343,7 @@
nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox);
}
-static JNINativeMethod gPdfEditor_Methods[] = {
+static const JNINativeMethod gPdfEditor_Methods[] = {
{"nativeOpen", "(IJ)J", (void*) nativeOpen},
{"nativeClose", "(J)V", (void*) nativeClose},
{"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp
index 006eef8..6ddfacf 100644
--- a/core/jni/android/graphics/pdf/PdfRenderer.cpp
+++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp
@@ -283,7 +283,7 @@
skBitmap.notifyPixelsChanged();
}
-static JNINativeMethod gPdfRenderer_Methods[] = {
+static const JNINativeMethod gPdfRenderer_Methods[] = {
{"nativeCreate", "(IJ)J", (void*) nativeCreate},
{"nativeClose", "(J)V", (void*) nativeClose},
{"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 40029bb..e045f5f 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -1087,18 +1087,18 @@
* JNI registration
*/
-static JNINativeMethod gMatrixMethods[] = {
+static const JNINativeMethod gMatrixMethods[] = {
{ "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
{ "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
};
-static JNINativeMethod gVisibilityMethods[] = {
+static const JNINativeMethod gVisibilityMethods[] = {
{ "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
{ "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
{ "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
};
-static JNINativeMethod gUtilsMethods[] = {
+static const JNINativeMethod gUtilsMethods[] = {
{ "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
{ "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
{ "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
@@ -1106,7 +1106,7 @@
{ "setTracingLevel", "(I)V", (void*)setTracingLevel },
};
-static JNINativeMethod gEtc1Methods[] = {
+static const JNINativeMethod gEtc1Methods[] = {
{ "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock },
{ "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock },
{ "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize },
@@ -1120,11 +1120,11 @@
typedef struct _ClassRegistrationInfo {
const char* classPath;
- JNINativeMethod* methods;
+ const JNINativeMethod* methods;
size_t methodCount;
} ClassRegistrationInfo;
-static ClassRegistrationInfo gClasses[] = {
+static const ClassRegistrationInfo gClasses[] = {
{"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
{"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)},
{"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
@@ -1136,7 +1136,7 @@
nativeClassInitBuffer(env);
int result = 0;
for (int i = 0; i < NELEM(gClasses); i++) {
- ClassRegistrationInfo* cri = &gClasses[i];
+ const ClassRegistrationInfo* cri = &gClasses[i];
result = RegisterMethodsOrDie(env, cri->classPath, cri->methods, cri->methodCount);
}
return result;
diff --git a/core/jni/android_animation_PropertyValuesHolder.cpp b/core/jni/android_animation_PropertyValuesHolder.cpp
index d1177418..6065c24 100644
--- a/core/jni/android_animation_PropertyValuesHolder.cpp
+++ b/core/jni/android_animation_PropertyValuesHolder.cpp
@@ -139,7 +139,7 @@
env->ReleaseIntArrayElements(arg, intValues, JNI_ABORT);
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nGetIntMethod", "(Ljava/lang/Class;Ljava/lang/String;)J",
(void*)android_animation_PropertyValuesHolder_getIntMethod },
{ "nGetFloatMethod", "(Ljava/lang/Class;Ljava/lang/String;)J",
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index ef17db6..36d78cf 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -76,7 +76,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)V",
(void*) android_content_res_ObbScanner_getObbInfo },
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 580ac02..bb09d00 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -477,7 +477,7 @@
return true;
}
-static JNINativeMethod sMethods[] =
+static const JNINativeMethod sMethods[] =
{
/* name, signature, funcPtr */
{ "nativeCreate", "(Ljava/lang/String;I)J",
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 7a3cdf6..bcc3bb0 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -786,7 +786,7 @@
}
-static JNINativeMethod sMethods[] =
+static const JNINativeMethod sMethods[] =
{
/* name, signature, funcPtr */
{ "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)J",
diff --git a/core/jni/android_database_SQLiteDebug.cpp b/core/jni/android_database_SQLiteDebug.cpp
index 26e13cf..4e4c36c 100644
--- a/core/jni/android_database_SQLiteDebug.cpp
+++ b/core/jni/android_database_SQLiteDebug.cpp
@@ -58,7 +58,7 @@
* JNI registration.
*/
-static JNINativeMethod gMethods[] =
+static const JNINativeMethod gMethods[] =
{
{ "nativeGetPagerStats", "(Landroid/database/sqlite/SQLiteDebug$PagerStats;)V",
(void*) nativeGetPagerStats },
diff --git a/core/jni/android_database_SQLiteGlobal.cpp b/core/jni/android_database_SQLiteGlobal.cpp
index 0a1c9f7..03e2387 100644
--- a/core/jni/android_database_SQLiteGlobal.cpp
+++ b/core/jni/android_database_SQLiteGlobal.cpp
@@ -75,7 +75,7 @@
return sqlite3_release_memory(SOFT_HEAP_LIMIT);
}
-static JNINativeMethod sMethods[] =
+static const JNINativeMethod sMethods[] =
{
/* name, signature, funcPtr */
{ "nativeReleaseMemory", "()I", (void*)nativeReleaseMemory },
diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
index ae96936..3e7a04e 100644
--- a/core/jni/android_ddm_DdmHandleNativeHeap.cpp
+++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
@@ -105,7 +105,7 @@
return array;
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{ "getLeakInfo", "()[B", (void*) DdmHandleNativeHeap_getLeakInfo },
};
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 5ddf235..32a877a 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -746,7 +746,7 @@
}; // namespace CanvasJNI
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
{"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
{"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp
index fd42ddb..03fcdef 100644
--- a/core/jni/android_graphics_Picture.cpp
+++ b/core/jni/android_graphics_Picture.cpp
@@ -91,7 +91,7 @@
pict->endRecording();
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"nativeGetWidth", "(J)I", (void*) android_graphics_Picture_getWidth},
{"nativeGetHeight", "(J)I", (void*) android_graphics_Picture_getHeight},
{"nativeConstructor", "(J)J", (void*) android_graphics_Picture_newPicture},
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 4f44c262..414eba7 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -949,7 +949,7 @@
//-------------------------------------------------
-static JNINativeMethod camMethods[] = {
+static const JNINativeMethod camMethods[] = {
{ "getNumberOfCameras",
"()I",
(void *)android_hardware_Camera_getNumberOfCameras },
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 7d0afdc..2e5cda0 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -343,7 +343,7 @@
}
//----------------------------------------------------------------------------
-static JNINativeMethod gSystemSensorManagerMethods[] = {
+static const JNINativeMethod gSystemSensorManagerMethods[] = {
{"nativeClassInit",
"()V",
(void*)nativeClassInit },
@@ -360,7 +360,7 @@
(void*)nativeIsDataInjectionEnabled},
};
-static JNINativeMethod gBaseEventQueueMethods[] = {
+static const JNINativeMethod gBaseEventQueueMethods[] = {
{"nativeInitBaseEventQueue",
"(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;ILjava/lang/String;)J",
(void*)nativeInitSensorEventQueue },
diff --git a/core/jni/android_hardware_SerialPort.cpp b/core/jni/android_hardware_SerialPort.cpp
index 2d2ff4d..393dc7b 100644
--- a/core/jni/android_hardware_SerialPort.cpp
+++ b/core/jni/android_hardware_SerialPort.cpp
@@ -243,7 +243,7 @@
tcsendbreak(fd, 0);
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{"native_open", "(Ljava/io/FileDescriptor;I)V",
(void *)android_hardware_SerialPort_open},
{"native_close", "()V", (void *)android_hardware_SerialPort_close},
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 1c4c9ec..048b3c7 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -768,14 +768,14 @@
return status;
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"listModules",
"(Ljava/util/ArrayList;)I",
(void *)android_hardware_SoundTrigger_listModules},
};
-static JNINativeMethod gModuleMethods[] = {
+static const JNINativeMethod gModuleMethods[] = {
{"native_setup",
"(Ljava/lang/Object;)V",
(void *)android_hardware_SoundTrigger_setup},
diff --git a/core/jni/android_hardware_UsbDevice.cpp b/core/jni/android_hardware_UsbDevice.cpp
index ef3b646..89d33e2 100644
--- a/core/jni/android_hardware_UsbDevice.cpp
+++ b/core/jni/android_hardware_UsbDevice.cpp
@@ -44,7 +44,7 @@
return result;
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
// static methods
{ "native_get_device_id", "(Ljava/lang/String;)I",
(void*)android_hardware_UsbDevice_get_device_id },
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index e0cae6f..1ba9fc5 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -246,7 +246,7 @@
return result;
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{"native_open", "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z",
(void *)android_hardware_UsbDeviceConnection_open},
{"native_close", "()V", (void *)android_hardware_UsbDeviceConnection_close},
diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp
index ce99e15..399e7b1 100644
--- a/core/jni/android_hardware_UsbRequest.cpp
+++ b/core/jni/android_hardware_UsbRequest.cpp
@@ -190,7 +190,7 @@
return (usb_request_cancel(request) == 0);
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{"native_init", "(Landroid/hardware/usb/UsbDeviceConnection;IIII)Z",
(void *)android_hardware_UsbRequest_init},
{"native_close", "()V", (void *)android_hardware_UsbRequest_close},
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index fb22689..7930027 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -529,7 +529,7 @@
//-------------------------------------------------
-static JNINativeMethod gCameraMetadataMethods[] = {
+static const JNINativeMethod gCameraMetadataMethods[] = {
// static methods
{ "nativeClassInit",
"()V",
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 8b69bbd..738a62a 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -2280,7 +2280,7 @@
} /*extern "C" */
-static JNINativeMethod gDngCreatorMethods[] = {
+static const JNINativeMethod gDngCreatorMethods[] = {
{"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
{"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
"Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 63915ed..f1ea7ec 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -730,7 +730,7 @@
} // extern "C"
-static JNINativeMethod gCameraDeviceMethods[] = {
+static const JNINativeMethod gCameraDeviceMethods[] = {
{ "nativeDetectSurfaceType",
"(Landroid/view/Surface;)I",
(void *)LegacyCameraDevice_nativeDetectSurfaceType },
diff --git a/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp b/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
index 7257597..f042058 100644
--- a/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
+++ b/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
@@ -302,7 +302,7 @@
} // extern "C"
-static JNINativeMethod gPerfMeasurementMethods[] = {
+static const JNINativeMethod gPerfMeasurementMethods[] = {
{ "nativeCreateContext",
"(I)J",
(jlong *)PerfMeasurement_nativeCreateContext },
diff --git a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
index 470c5ba..4b279f63 100644
--- a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
+++ b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
@@ -275,7 +275,7 @@
}
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
// {"name", "signature", (void*) functionPointer },
{ "nativeClassInit", "()V", (void*) class_init },
{ "nativeInitialize", "()V", (void*) initialize },
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 6c2bbd4..b977e37 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -682,7 +682,7 @@
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
{"native_start", "(II)I", (void *)android_media_AudioRecord_start},
{"native_stop", "()V", (void *)android_media_AudioRecord_stop},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 91b3278..6d3c7d7 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1616,7 +1616,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"setParameters", "(Ljava/lang/String;)I", (void *)android_media_AudioSystem_setParameters},
{"getParameters", "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters},
{"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone},
@@ -1663,7 +1663,7 @@
};
-static JNINativeMethod gEventHandlerMethods[] = {
+static const JNINativeMethod gEventHandlerMethods[] = {
{"native_setup",
"(Ljava/lang/Object;)V",
(void *)android_media_AudioSystem_eventHandlerSetup},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 5faa150..7860b74 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1052,7 +1052,7 @@
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
{"native_start", "()V", (void *)android_media_AudioTrack_start},
{"native_stop", "()V", (void *)android_media_AudioTrack_stop},
diff --git a/core/jni/android_media_JetPlayer.cpp b/core/jni/android_media_JetPlayer.cpp
index d441f10..873c3f2 100644
--- a/core/jni/android_media_JetPlayer.cpp
+++ b/core/jni/android_media_JetPlayer.cpp
@@ -488,7 +488,7 @@
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
{"native_setup", "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
{"native_finalize", "()V", (void *)android_media_JetPlayer_finalize},
diff --git a/core/jni/android_media_RemoteDisplay.cpp b/core/jni/android_media_RemoteDisplay.cpp
index 9bc223b..bd1a6ec 100644
--- a/core/jni/android_media_RemoteDisplay.cpp
+++ b/core/jni/android_media_RemoteDisplay.cpp
@@ -177,7 +177,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"nativeListen", "(Ljava/lang/String;Ljava/lang/String;)J",
(void*)nativeListen },
{"nativeDispose", "(J)V",
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 243f040..aec6263 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -123,7 +123,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone },
{ "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone },
{ "getAudioSessionId", "()I", (void *)android_media_ToneGenerator_getAudioSessionId},
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index c137b17..d6d4310 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -495,7 +495,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
(void*)socket_connect_local},
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index fada7ac2..ba0876d 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -302,7 +302,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gNetworkUtilMethods[] = {
+static const JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
{ "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections },
{ "startDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_startDhcp },
diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp
index 7354417..7b7d0cf 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/core/jni/android_net_TrafficStats.cpp
@@ -185,7 +185,7 @@
}
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"nativeGetTotalStat", "(I)J", (void*) getTotalStat},
{"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat},
{"nativeGetUidStat", "(II)J", (void*) getUidStat},
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 9f5b3bc..568473c 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -1231,7 +1231,7 @@
static const char *classPathName = "android/opengl/EGL14";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"eglGetError", "()I", (void *) android_eglGetError },
{"eglGetDisplay", "(I)Landroid/opengl/EGLDisplay;", (void *) android_eglGetDisplayInt },
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index 60a3bf6..62ccad4 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -149,7 +149,7 @@
static const char *classPathName = "android/opengl/EGLExt";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"eglPresentationTimeANDROID", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;J)Z", (void *) android_eglPresentationTimeANDROID },
};
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index dac98de..f4135c2 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -3184,7 +3184,7 @@
static const char *classPathName = "android/opengl/GLES10";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"glActiveTexture", "(I)V", (void *) android_glActiveTexture__I },
{"glAlphaFunc", "(IF)V", (void *) android_glAlphaFunc__IF },
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index 95be11b..4dc4233 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -582,7 +582,7 @@
static const char *classPathName = "android/opengl/GLES10Ext";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"glQueryMatrixxOES", "([II[II)I", (void *) android_glQueryMatrixxOES___3II_3II },
{"glQueryMatrixxOES", "(Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;)I", (void *) android_glQueryMatrixxOES__Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2 },
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 6970a3c..2625e03 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -3065,7 +3065,7 @@
static const char *classPathName = "android/opengl/GLES11";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"glBindBuffer", "(II)V", (void *) android_glBindBuffer__II },
{"glBufferData", "(IILjava/nio/Buffer;I)V", (void *) android_glBufferData__IILjava_nio_Buffer_2I },
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 6422ff2..fb85cb0 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -3573,7 +3573,7 @@
static const char *classPathName = "android/opengl/GLES11Ext";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"glBlendEquationSeparateOES", "(II)V", (void *) android_glBlendEquationSeparateOES__II },
{"glBlendFuncSeparateOES", "(IIII)V", (void *) android_glBlendFuncSeparateOES__IIII },
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index f9a0dfe..b9f61a9 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -6152,7 +6152,7 @@
static const char *classPathName = "android/opengl/GLES20";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"glActiveTexture", "(I)V", (void *) android_glActiveTexture__I },
{"glAttachShader", "(II)V", (void *) android_glAttachShader__II },
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index 1d92cd4..8eb5044 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -5167,7 +5167,7 @@
static const char *classPathName = "android/opengl/GLES30";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"glReadBuffer", "(I)V", (void *) android_glReadBuffer__I },
{"glDrawRangeElements", "(IIIIILjava/nio/Buffer;)V", (void *) android_glDrawRangeElements__IIIIILjava_nio_Buffer_2 },
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index 92ecbe0..e427388 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -3175,7 +3175,7 @@
static const char *classPathName = "android/opengl/GLES31";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"glDispatchCompute", "(III)V", (void *) android_glDispatchCompute__III },
{"glDispatchComputeIndirect", "(J)V", (void *) android_glDispatchComputeIndirect },
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 2856308..180c693 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -1443,7 +1443,7 @@
static const char *classPathName = "android/opengl/GLES31Ext";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"glBlendBarrierKHR", "()V", (void *) android_glBlendBarrierKHR__ },
{"glDebugMessageControlKHR", "(IIII[IIZ)V", (void *) android_glDebugMessageControlKHR__IIII_3IIZ },
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index b969477..097bbac 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -973,7 +973,7 @@
* JNI registration.
*/
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "getNativeHeapSize", "()J",
(void*) android_os_Debug_getNativeHeapSize },
{ "getNativeHeapAllocatedSize", "()J",
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index d2db3c9..e57a719 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -209,7 +209,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMessageQueueMethods[] = {
+static const JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
{ "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 762b88f..8ba77ae 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -320,7 +320,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
/* name, signature, funcPtr */
{ "checkSELinuxAccess" , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
{ "getContext" , "()Ljava/lang/String;" , (void*)getCon },
diff --git a/core/jni/android_os_SystemClock.cpp b/core/jni/android_os_SystemClock.cpp
index dfe024e..d98407d 100644
--- a/core/jni/android_os_SystemClock.cpp
+++ b/core/jni/android_os_SystemClock.cpp
@@ -104,7 +104,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "uptimeMillis", "()J",
(void*) android_os_SystemClock_uptimeMillis },
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index 554d304..5dace6b 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -220,7 +220,7 @@
}
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{ "native_get", "(Ljava/lang/String;)Ljava/lang/String;",
(void*) SystemProperties_getS },
{ "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 3fd3b3c..30fc47b 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -105,7 +105,7 @@
atrace_set_tracing_enabled(enabled);
}
-static JNINativeMethod gTraceMethods[] = {
+static const JNINativeMethod gTraceMethods[] = {
/* name, signature, funcPtr */
{ "nativeGetEnabledTags",
"()J",
diff --git a/core/jni/android_os_UEventObserver.cpp b/core/jni/android_os_UEventObserver.cpp
index eb36f85..30d40a2 100644
--- a/core/jni/android_os_UEventObserver.cpp
+++ b/core/jni/android_os_UEventObserver.cpp
@@ -103,7 +103,7 @@
}
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nativeSetup", "()V",
(void *)nativeSetup },
{ "nativeWaitForNextEvent", "()Ljava/lang/String;",
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
index ca21fd7..818bf53 100644
--- a/core/jni/android_server_NetworkManagementSocketTagger.cpp
+++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp
@@ -83,7 +83,7 @@
return (jint)res;
}
-static JNINativeMethod gQTagUidMethods[] = {
+static const JNINativeMethod gQTagUidMethods[] = {
{ "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)QTagUid_tagSocketFd},
{ "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)QTagUid_untagSocketFd},
{ "native_setCounterSet", "(II)I", (void*)QTagUid_setCounterSet},
diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
index 3285429..2a3f036 100644
--- a/core/jni/android_text_AndroidBidi.cpp
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -56,7 +56,7 @@
return result;
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "runBidi", "(I[C[BIZ)I", (void*) runBidi }
};
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 9258248..474a74e 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -178,7 +178,7 @@
return u_charMirror(c);
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "getDirectionalities", "([C[BI)V",
(void*) getDirectionalities },
{ "getEastAsianWidth", "(C)I",
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 90e4bb6..a151e00 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -172,7 +172,7 @@
env->SetFloatArrayRegion(widths, 0, b->size(), b->charWidths());
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
// TODO performance: many of these are candidates for fast jni, awaiting guidance
{"nNewBuilder", "()J", (void*) nNewBuilder},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 7ca0654..614e82f 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -2112,7 +2112,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gAssetManagerMethods[] = {
+static const JNINativeMethod gAssetManagerMethods[] = {
/* name, signature, funcPtr */
// Basic asset stuff.
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 05bc125..4f8a2cb 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -249,7 +249,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gRegisterMethods[] = {
+static const JNINativeMethod gRegisterMethods[] = {
/* name, signature, funcPtr */
{ "writeEvent", "(II)I", (void*) android_util_EventLog_writeEvent_Integer },
{ "writeEvent", "(IJ)I", (void*) android_util_EventLog_writeEvent_Long },
diff --git a/core/jni/android_util_FileObserver.cpp b/core/jni/android_util_FileObserver.cpp
index 067d298..2b93b6d 100644
--- a/core/jni/android_util_FileObserver.cpp
+++ b/core/jni/android_util_FileObserver.cpp
@@ -127,7 +127,7 @@
#endif
}
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{ "init", "()I", (void*)android_os_fileobserver_init },
{ "observe", "(I)V", (void*)android_os_fileobserver_observe },
diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp
index 2b1067b..2d23cda 100644
--- a/core/jni/android_util_Log.cpp
+++ b/core/jni/android_util_Log.cpp
@@ -111,7 +111,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
{ "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index f83eaec..b396afe 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -155,7 +155,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gStringBlockMethods[] = {
+static const JNINativeMethod gStringBlockMethods[] = {
/* name, signature, funcPtr */
{ "nativeCreate", "([BII)J",
(void*) android_content_StringBlock_nativeCreate },
diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp
index 375710e..7ae51c8 100644
--- a/core/jni/android_util_XmlBlock.cpp
+++ b/core/jni/android_util_XmlBlock.cpp
@@ -364,7 +364,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gXmlBlockMethods[] = {
+static const JNINativeMethod gXmlBlockMethods[] = {
/* name, signature, funcPtr */
{ "nativeCreate", "([BII)J",
(void*) android_content_XmlBlock_nativeCreate },
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 0e2ec6b..437bd19 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -259,7 +259,7 @@
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
"(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J",
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 17d2a5e..7682dd6 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -268,7 +268,7 @@
const char* const kClassPathName = "android/view/GraphicBuffer";
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nCreateGraphicBuffer", "(IIII)J", (void*) android_view_GraphiceBuffer_create },
{ "nDestroyGraphicBuffer", "(J)V", (void*) android_view_GraphiceBuffer_destroy },
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index 36ba892..3a0ddc9 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -85,7 +85,7 @@
const char* const kClassPathName = "android/view/HardwareLayer";
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nPrepare", "(JIIZ)Z", (void*) android_view_HardwareLayer_prepare },
{ "nSetLayerPaint", "(JJ)V", (void*) android_view_HardwareLayer_setLayerPaint },
{ "nSetTransform", "(JJ)V", (void*) android_view_HardwareLayer_setTransform },
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 4b42ab5..092ac27 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -259,7 +259,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gInputChannelMethods[] = {
+static const JNINativeMethod gInputChannelMethods[] = {
/* name, signature, funcPtr */
{ "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;",
(void*)android_view_InputChannel_nativeOpenInputChannelPair },
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 43b8471..8293cd8 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -395,7 +395,7 @@
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
"(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index d61dee7..3bd6917 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -289,7 +289,7 @@
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
"(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index 7653f58..e5519a7 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -203,7 +203,7 @@
* JNI registration.
*/
-static JNINativeMethod g_methods[] = {
+static const JNINativeMethod g_methods[] = {
/* name, signature, funcPtr */
{ "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
(void*)nativeReadFromParcel },
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 98c17c0..81d46c3 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -752,7 +752,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMotionEventMethods[] = {
+static const JNINativeMethod gMotionEventMethods[] = {
/* name, signature, funcPtr */
{ "nativeInitialize",
"(JIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index a3b3700..388d365 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -467,7 +467,7 @@
const char* const kClassPathName = "android/view/RenderNode";
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nCreate", "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
{ "nDestroyRenderNode", "(J)V", (void*) android_view_RenderNode_destroyRenderNode },
{ "nSetDisplayListData", "(JJ)V", (void*) android_view_RenderNode_setDisplayListData },
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index 4177ee2..0926e9b 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -193,7 +193,7 @@
const char* const kClassPathName = "android/view/RenderNodeAnimator";
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nCreateAnimator", "(IF)J", (void*) createAnimator },
{ "nCreateCanvasPropertyFloatAnimator", "(JF)J", (void*) createCanvasPropertyFloatAnimator },
{ "nCreateCanvasPropertyPaintAnimator", "(JIF)J", (void*) createCanvasPropertyPaintAnimator },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4a311d31..24055e7 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -514,7 +514,7 @@
namespace hwui = android::uirenderer;
-static JNINativeMethod gSurfaceMethods[] = {
+static const JNINativeMethod gSurfaceMethods[] = {
{"nativeCreateFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)J",
(void*)nativeCreateFromSurfaceTexture },
{"nativeRelease", "(J)V",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index d1acb59..65ebb663 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -572,7 +572,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod sSurfaceControlMethods[] = {
+static const JNINativeMethod sSurfaceControlMethods[] = {
{"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIII)J",
(void*)nativeCreate },
{"nativeRelease", "(J)V",
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 609c565..dad6958 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -56,7 +56,7 @@
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeCreate", "()J",
(void*)nativeCreate },
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index beb83b1..b736a17 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -197,7 +197,7 @@
const char* const kClassPathName = "android/view/TextureView";
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nCreateNativeWindow", "(Landroid/graphics/SurfaceTexture;)V",
(void*) android_view_TextureView_createNativeWindow },
{ "nDestroyNativeWindow", "()V",
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 6c3676b..c79f833 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -440,6 +440,32 @@
}
}
+static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ proxy->addRenderNode(renderNode, placeFront);
+}
+
+static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jlong renderNodePtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ proxy->removeRenderNode(renderNode);
+}
+
+static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jlong renderNodePtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ proxy->drawRenderNode(renderNode);
+}
+
+static void android_view_ThreadedRenderer_setContentOverdrawProtectionBounds(JNIEnv* env,
+ jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ proxy->setContentOverdrawProtectionBounds(left, top, right, bottom);
+}
// ----------------------------------------------------------------------------
// Shaders
@@ -447,7 +473,6 @@
static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
jstring diskCachePath) {
-
const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
egl_cache_t::get()->setCacheFilename(cacheArray);
env->ReleaseStringUTFChars(diskCachePath, cacheArray);
@@ -459,7 +484,7 @@
const char* const kClassPathName = "android/view/ThreadedRenderer";
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V", (void*) android_view_ThreadedRenderer_setAtlas },
{ "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
{ "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
@@ -494,6 +519,11 @@
{ "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData },
{ "setupShadersDiskCache", "(Ljava/lang/String;)V",
(void*) android_view_ThreadedRenderer_setupShadersDiskCache },
+ { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
+ { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
+ { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
+ { "nSetContentOverdrawProtectionBounds", "(JIIII)V",
+ (void*)android_view_ThreadedRenderer_setContentOverdrawProtectionBounds},
};
int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index ddd5fc8..04ec705 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -215,7 +215,7 @@
// --- JNI Registration ---
-static JNINativeMethod gVelocityTrackerMethods[] = {
+static const JNINativeMethod gVelocityTrackerMethods[] = {
/* name, signature, funcPtr */
{ "nativeInitialize",
"(Ljava/lang/String;)J",
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index daa6f82..364ac44 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -561,7 +561,7 @@
delete reinterpret_cast<ZipFileRO*>(apkHandle);
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"nativeOpenApk",
"(Ljava/lang/String;)J",
(void *)com_android_internal_content_NativeLibraryHelper_openApk},
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index 6c0b756..70134ab 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -284,7 +284,7 @@
return 0;
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nativeReadNetworkStatsDetail",
"(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;I)I",
(void*) readNetworkStatsDetail }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index aef70be..3f1be456 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -647,7 +647,7 @@
return pid;
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
diff --git a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
index 7a18c2d..d20bae2 100644
--- a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
+++ b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
@@ -36,7 +36,7 @@
const char* const kClassPathName = "com/android/internal/util/VirtualRefBasePtr";
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nIncStrong", "(J)V", (void*) incStrong },
{ "nDecStrong", "(J)V", (void*) decStrong },
};
diff --git a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
index 2c65d62..6781e13 100644
--- a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
+++ b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
@@ -78,7 +78,7 @@
const char* const kClassPathName = "com/android/internal/view/animation/NativeInterpolatorFactoryHelper";
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator },
{ "createAccelerateInterpolator", "(F)J", (void*) createAccelerateInterpolator },
{ "createAnticipateInterpolator", "(F)J", (void*) createAnticipateInterpolator },
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index baeb7dd..3d63b01 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -532,7 +532,7 @@
#define OBJECT "Ljava/lang/Object;"
#define STRING "Ljava/lang/String;"
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit","()V", (void*)nativeClassInit },
{"eglWaitGL", "()Z", (void*)jni_eglWaitGL },
{"eglInitialize", "(" DISPLAY "[I)Z", (void*)jni_eglInitialize },
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index f15f957..ad7d744 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -8490,7 +8490,7 @@
static const char *classPathName = "com/google/android/gles_jni/GLImpl";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nativeClassInit", "()V", (void*)nativeClassInit },
{"glActiveTexture", "(I)V", (void *) android_glActiveTexture__I },
{"glAlphaFunc", "(IF)V", (void *) android_glAlphaFunc__IF },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 921385d..148e7c1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -65,6 +65,7 @@
<protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
<protected-broadcast android:name="android.intent.action.REBOOT" />
<protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
+ <protected-broadcast android:name="android.intent.action.THERMAL_EVENT" />
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
<protected-broadcast android:name="android.intent.action.USER_ADDED" />
<protected-broadcast android:name="android.intent.action.USER_REMOVED" />
@@ -188,7 +189,7 @@
<protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
<protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
- <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
@@ -1714,7 +1715,7 @@
android:protectionLevel="signature|privileged" />
<!-- Allows applications to change network connectivity state.
- <p>Protection level: normal
+ <p>Protection level: signature
-->
<permission android:name="android.permission.CHANGE_NETWORK_STATE"
android:description="@string/permdesc_changeNetworkState"
@@ -2175,6 +2176,13 @@
<permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
android:protectionLevel="signature" />
+ <!-- Allows an application to control the color transforms applied to
+ displays system-wide.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
@@ -2522,6 +2530,12 @@
android:label="@string/permlab_access_notification_policy"
android:protectionLevel="normal" />
+ <!-- Allows modification of do not disturb rules and policies. Only allowed for system
+ processes.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_NOTIFICATIONS"
+ android:protectionLevel="signature" />
+
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
diff --git a/core/res/res/color/btn_colored_material.xml b/core/res/res/color/btn_colored_background_material.xml
similarity index 87%
rename from core/res/res/color/btn_colored_material.xml
rename to core/res/res/color/btn_colored_background_material.xml
index b45f824..b7f0ef5 100644
--- a/core/res/res/color/btn_colored_material.xml
+++ b/core/res/res/color/btn_colored_background_material.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<!-- Used for tha background of a bordered colored button. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
diff --git a/core/res/res/color/btn_colored_material.xml b/core/res/res/color/btn_colored_borderless_text_material.xml
similarity index 78%
copy from core/res/res/color/btn_colored_material.xml
copy to core/res/res/color/btn_colored_borderless_text_material.xml
index b45f824..ee5a90c 100644
--- a/core/res/res/color/btn_colored_material.xml
+++ b/core/res/res/color/btn_colored_borderless_text_material.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,9 +14,10 @@
limitations under the License.
-->
+<!-- Used for the text of a borderless colored button. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
- android:color="?attr/colorButtonNormal" />
- <item android:color="?attr/colorAccent" />
+ android:color="?attr/textColorSecondary" />
+ <item android:color="?attr/colorAccent"/>
</selector>
diff --git a/core/res/res/color/btn_colored_text_material.xml b/core/res/res/color/btn_colored_text_material.xml
index 950d04a..23d05a9 100644
--- a/core/res/res/color/btn_colored_text_material.xml
+++ b/core/res/res/color/btn_colored_text_material.xml
@@ -14,9 +14,10 @@
limitations under the License.
-->
+<!-- Used for the text of a bordered colored button. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
android:color="?attr/textColorSecondary" />
- <item android:color="?attr/colorAccent"/>
+ <item android:color="?attr/textColorSecondaryInverse" />
</selector>
diff --git a/core/res/res/drawable/btn_colored_material.xml b/core/res/res/drawable/btn_colored_material.xml
index 81cbe39..c3c5760 100644
--- a/core/res/res/drawable/btn_colored_material.xml
+++ b/core/res/res/drawable/btn_colored_material.xml
@@ -22,7 +22,7 @@
<ripple android:color="?attr/colorControlHighlight">
<item>
<shape android:shape="rectangle"
- android:tint="@color/btn_colored_material">
+ android:tint="@color/btn_colored_background_material">
<corners android:radius="@dimen/control_corner_material" />
<solid android:color="@color/white" />
<padding android:left="@dimen/button_padding_horizontal_material"
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 2473e87..bb347cb 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -46,7 +46,8 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerInParent="true"
- android:paddingTop="@dimen/timepicker_radial_picker_top_margin">
+ android:paddingTop="@dimen/timepicker_radial_picker_top_margin"
+ android:layout_marginBottom="-12dp">
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
@@ -57,14 +58,16 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right" />
+ android:gravity="right"
+ android:includeFontPadding="false" />
<TextView
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
- android:importantForAccessibility="no" />
+ android:importantForAccessibility="no"
+ android:includeFontPadding="false" />
<!-- The minutes should always be to the right of the separator,
regardless of the current locale's layout direction. -->
@@ -75,7 +78,8 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left" />
+ android:gravity="left"
+ android:includeFontPadding="false" />
</LinearLayout>
<!-- The layout alignment of this view will switch between toRightOf
@@ -93,22 +97,27 @@
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:minWidth="48dp"
+ android:gravity="bottom"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingTop="@dimen/timepicker_am_top_padding"
+ android:paddingTop="4dp"
+ android:paddingBottom="6dp"
android:lines="1"
- android:ellipsize="none"
- android:includeFontPadding="false" />
+ android:ellipsize="none" />
<CheckedTextView
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:minWidth="48dp"
+ android:gravity="top"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingTop="@dimen/timepicker_pm_top_padding"
android:lines="1"
android:ellipsize="none"
android:includeFontPadding="false" />
diff --git a/core/res/res/layout/calendar_view.xml b/core/res/res/layout/calendar_view.xml
index bccb056..5b32a392 100644
--- a/core/res/res/layout/calendar_view.xml
+++ b/core/res/res/layout/calendar_view.xml
@@ -28,7 +28,7 @@
android:layout_gravity="center_horizontal"
android:paddingTop="10dip"
android:paddingBottom="10dip"
- style="@android:style/TextAppearance.Medium" />
+ style="?android:attr/textAppearanceMedium" />
<LinearLayout android:id="@+android:id/day_names"
android:orientation="horizontal"
diff --git a/core/res/res/color/btn_colored_material.xml b/core/res/res/layout/docked_stack_divider.xml
similarity index 65%
copy from core/res/res/color/btn_colored_material.xml
copy to core/res/res/layout/docked_stack_divider.xml
index b45f824..aa6e68d 100644
--- a/core/res/res/color/btn_colored_material.xml
+++ b/core/res/res/layout/docked_stack_divider.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,9 +14,8 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?attr/disabledAlpha"
- android:color="?attr/colorButtonNormal" />
- <item android:color="?attr/colorAccent" />
-</selector>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/docked_stack_divider_thickness"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ />
diff --git a/core/res/res/layout/number_picker_material.xml b/core/res/res/layout/number_picker_material.xml
index 47edec4..b045585 100644
--- a/core/res/res/layout/number_picker_material.xml
+++ b/core/res/res/layout/number_picker_material.xml
@@ -22,4 +22,4 @@
android:gravity="center"
android:singleLine="true"
android:background="@null"
- android:textAppearance="@style/TextAppearance.Material.Caption" />
+ android:textAppearance="@style/TextAppearance.Material.Body1" />
diff --git a/core/res/res/layout/popup_menu_header_item_layout.xml b/core/res/res/layout/popup_menu_header_item_layout.xml
new file mode 100644
index 0000000..5879f85
--- /dev/null
+++ b/core/res/res/layout/popup_menu_header_item_layout.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/dropdownListPreferredItemHeight"
+ android:minWidth="196dip"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/textAppearancePopupMenuHeader"
+ android:layout_gravity="center_vertical"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:textAlignment="viewStart" />
+
+</FrameLayout>
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index 3c3a8a8..acdc509 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -78,6 +78,9 @@
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:gravity="bottom"
android:paddingTop="@dimen/timepicker_am_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
@@ -86,6 +89,9 @@
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:gravity="top"
android:paddingTop="@dimen/timepicker_pm_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index cd6bb4b..7b05612 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -231,7 +231,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakty"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"přístup ke kontaktům"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"přístup k poloze tohoto zařízení"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"přístup k poloze tohoto zařízení"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendář"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"přístup ke kalendáři"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index e718935..60f0502 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -493,8 +493,8 @@
<string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"Dette giver indehaveren mulighed for at knytte sig til det øverste grænsefladeniveau for et mobilselskabs beskedtjeneste. Dette bør ikke være nødvendigt i normale apps."</string>
<string name="permlab_bindCarrierServices" msgid="3233108656245526783">"knytte til tjenester fra mobilselskabet"</string>
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Tillader, at brugeren knytter sig til tjenester fra mobilselskabet. Dette bør aldrig være nødvendigt for almindelige apps."</string>
- <string name="permlab_access_notification_policy" msgid="4247510821662059671">"Adgang til Vil ikke forstyrres"</string>
- <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Giver appen tilladelse til at læse og skrive konfigurationen af Vil ikke forstyrres."</string>
+ <string name="permlab_access_notification_policy" msgid="4247510821662059671">"have adgang til Forstyr ikke"</string>
+ <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Giver appen tilladelse til at læse og skrive konfigurationen af Forstyr ikke."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Angiv regler for adgangskoder"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Kontrollér længden samt tilladte tegn i adgangskoder og pinkoder til skærmlåsen."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Overvåg forsøg på oplåsning af skærm"</string>
@@ -1471,10 +1471,10 @@
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Indtil du slår denne indstilling fra"</string>
- <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Indtil du slår \"Vil ikke forstyrres\" fra"</string>
+ <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Indtil du slår \"Forstyr ikke\" fra"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="2821479483960330739">"Skjul"</string>
- <string name="zen_mode_feature_name" msgid="5254089399895895004">"Vil ikke forstyrres"</string>
+ <string name="zen_mode_feature_name" msgid="5254089399895895004">"Forstyr ikke"</string>
<string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"Nedetid"</string>
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Hverdagsaften"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 9b65882..bb44ed3 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -890,7 +890,7 @@
<string name="whichHomeApplication" msgid="4307587691506919691">"Selecciona una aplicación de inicio"</string>
<string name="whichHomeApplicationNamed" msgid="4493438593214760979">"Usar %1$s como aplicación de inicio"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Usar siempre para esta acción"</string>
- <string name="use_a_different_app" msgid="8134926230585710243">"Uitliza otra aplicación"</string>
+ <string name="use_a_different_app" msgid="8134926230585710243">"Utiliza otra aplicación"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"Para borrar los valores predeterminados, accede a Ajustes del sistema > Aplicaciones > Descargadas."</string>
<string name="chooseActivity" msgid="7486876147751803333">"Selecciona una acción"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"Elegir una aplicación para el dispositivo USB"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 2c7a64d..8e7193a 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -227,23 +227,23 @@
<string name="user_owner_label" msgid="2804351898001038951">"Pertsonalak"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"Lana"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktuak"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"Atzitu kontaktuak"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"kontaktuak atzitzeko"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Kokapena"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"Atzitu gailuaren kokapena"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"gailuaren kokapena atzitzeko"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Egutegia"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"Atzitu egutegia"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"egutegia atzitzeko"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS mezuak"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"Bidali eta ikusi SMS mezuak"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS mezuak bidaltzeko eta ikusteko"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Memoria"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"Atzitu gailuko argazkiak, multimedia-elementuak eta fitxategiak"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"gailuko argazkiak, multimedia-elementuak eta fitxategiak atzitzeko"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofonoa"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"Grabatu audioa"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"audioa grabatzeko"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"Atera argazkiak eta grabatu bideoak"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"argazkiak ateratzeko eta bideoak grabatzeko"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefonoa"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"Egin eta kudeatu telefono-deiak"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"telefono-deiak egiteko eta kudeatzeko"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Gorputz-sentsoreak"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"Atzitu bizi-konstanteei buruzko sentsore-datuak"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"bizi-konstanteei buruzko sentsore-datuak atzitzeko"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Eskuratu leihoko edukia"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Arakatu irekita daukazun leihoko edukia."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Aktibatu ukipen bidez arakatzeko eginbidea"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 2e3c569..45d9dc3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -144,14 +144,14 @@
<string name="httpErrorOk" msgid="1191919378083472204">"تأیید"</string>
<string name="httpError" msgid="7956392511146698522">"خطایی در شبکه وجود داشت."</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"URL پیدا نشد."</string>
- <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"طرح کلی تأیید اعتبار سایت پشتیبانی نمیشود."</string>
+ <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"طرح کلی تأیید اعتبار سایت پشتیبانی نمیشود."</string>
<string name="httpErrorAuth" msgid="1435065629438044534">"تأیید اعتبار انجام نشد."</string>
<string name="httpErrorProxyAuth" msgid="1788207010559081331">"تأیید اعتبار از طریق سرور پروکسی انجام نشد."</string>
<string name="httpErrorConnect" msgid="8714273236364640549">"اتصال به سرور انجام نشد."</string>
<string name="httpErrorIO" msgid="2340558197489302188">"برقراری ارتباط با سرور ممکن نبود. بعداً دوباره امتحان کنید."</string>
<string name="httpErrorTimeout" msgid="4743403703762883954">"زمان اتصال به سرور تمام شده است."</string>
<string name="httpErrorRedirectLoop" msgid="8679596090392779516">"این صفحه دارای تعداد بسیار زیادی تغییر مسیر سرور است."</string>
- <string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"پروتکل پشتیبانی نمیشود."</string>
+ <string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"پروتکل پشتیبانی نمیشود."</string>
<string name="httpErrorFailedSslHandshake" msgid="96549606000658641">"اتصال امن ایجاد نشد."</string>
<string name="httpErrorBadUrl" msgid="3636929722728881972">"بدلیل نامعتبر بودن URL، باز کردن صفحه ممکن نیست."</string>
<string name="httpErrorFile" msgid="2170788515052558676">"دسترسی به فایل انجام نشد."</string>
@@ -781,13 +781,13 @@
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"به برنامه اجازه میدهد تا سابقه یا نشانکهای ذخیره شده مرورگر در تلویزیون شما را تغییر دهد. شاید به برنامه اجازه دهد تا دادههای «مرورگر» را پاک کند یا تغییر دهد. توجه: این مجوز شاید توسط مرورگرهای شخص ثالث یا سایر برنامهها با قابلیتهای مرور وب اجرا شود."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"به برنامه اجازه میدهد سابقه مرورگر یا نشانکهای ذخیره شده در تلفن شما را اصلاح کند. این ویژگی ممکن است به برنامه اجازه دهد دادههای مرورگر را حذف یا اصلاح کند. توجه: این مجوز ممکن است توسط مرورگرهای شخص ثالث یا سایر برنامههای دارای قابلیت مرور وب قابل اجرا نباشد."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"تنظیم یک هشدار"</string>
- <string name="permdesc_setAlarm" msgid="316392039157473848">"به برنامه اجازه میدهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامههای ساعت زنگدار نمیتوانند این ویژگی را اعمال کنند."</string>
+ <string name="permdesc_setAlarm" msgid="316392039157473848">"به برنامه اجازه میدهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامههای ساعت زنگدار نمیتوانند این ویژگی را اعمال کنند."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"افزودن پست صوتی"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه میدهد تا پیامها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"به برنامه اجازه میدهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامههای مخرب میتوانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایتهای وب کتابخانه بفرستند."</string>
<string name="save_password_message" msgid="767344687139195790">"میخواهید مرورگر این رمز ورود را به خاطر داشته باشد؟"</string>
- <string name="save_password_notnow" msgid="6389675316706699758">"الآن نه"</string>
+ <string name="save_password_notnow" msgid="6389675316706699758">"اکنون نه"</string>
<string name="save_password_remember" msgid="6491879678996749466">"به خاطر سپردن"</string>
<string name="save_password_never" msgid="8274330296785855105">"هیچوقت"</string>
<string name="open_permission_deny" msgid="7374036708316629800">"شما اجازه بازکردن این صفحه را ندارید."</string>
@@ -894,16 +894,16 @@
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"پیشفرض را در تنظیمات سیستم> برنامهها> مورد دانلود شده پاک کنید."</string>
<string name="chooseActivity" msgid="7486876147751803333">"انتخاب عملکرد"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"انتخاب برنامه برای دستگاه USB"</string>
- <string name="noApplications" msgid="2991814273936504689">"هیچ برنامهای نمیتواند این کار را انجام دهد."</string>
+ <string name="noApplications" msgid="2991814273936504689">"هیچ برنامهای نمیتواند این کار را انجام دهد."</string>
<string name="aerr_title" msgid="1905800560317137752"></string>
<string name="aerr_application" msgid="932628488013092776">"متأسفانه، <xliff:g id="APPLICATION">%1$s</xliff:g> متوقف شده است."</string>
<string name="aerr_process" msgid="4507058997035697579">"متأسفانه، پردازش <xliff:g id="PROCESS">%1$s</xliff:g> متوقف شده است."</string>
<string name="aerr_process_silence" msgid="4226685530196000222">"تا راهاندازی مجدد، خرابیها از <xliff:g id="PROCESS">%1$s</xliff:g> نادیده گرفته شوند."</string>
<string name="anr_title" msgid="4351948481459135709"></string>
- <string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> پاسخ نمیدهد.\n\nآیا میخواهید آنرا ببندید؟"</string>
- <string name="anr_activity_process" msgid="5776209883299089767">"فعالیت <xliff:g id="ACTIVITY">%1$s</xliff:g> پاسخ نمیدهد.\n\nآیا میخواهید آن را ببندید؟"</string>
- <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> پاسخ نمیدهد. آیا میخواهید آن را ببندید؟"</string>
- <string name="anr_process" msgid="6513209874880517125">"روند <xliff:g id="PROCESS">%1$s</xliff:g> پاسخ نمیدهد. \n\nآیا میخواهید آن را ببندید؟"</string>
+ <string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> پاسخ نمیدهد.\n\nآیا میخواهید آنرا ببندید؟"</string>
+ <string name="anr_activity_process" msgid="5776209883299089767">"فعالیت <xliff:g id="ACTIVITY">%1$s</xliff:g> پاسخ نمیدهد.\n\nآیا میخواهید آن را ببندید؟"</string>
+ <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> پاسخ نمیدهد. آیا میخواهید آن را ببندید؟"</string>
+ <string name="anr_process" msgid="6513209874880517125">"روند <xliff:g id="PROCESS">%1$s</xliff:g> پاسخ نمیدهد. \n\nآیا میخواهید آن را ببندید؟"</string>
<string name="force_close" msgid="8346072094521265605">"تأیید"</string>
<string name="report" msgid="4060218260984795706">"گزارش"</string>
<string name="wait" msgid="7147118217226317732">"منتظر بمانید"</string>
@@ -1006,14 +1006,14 @@
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"همیشه غیرمجاز"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"سیم کارت برداشته شد"</string>
<string name="sim_removed_message" msgid="5450336489923274918">"تا زمانی که با یک سیمکارت معتبر دوباره راهاندازی نکنید شبکه تلفن همراه غیر قابل دسترس خواهد بود."</string>
- <string name="sim_done_button" msgid="827949989369963775">"انجام شد"</string>
+ <string name="sim_done_button" msgid="827949989369963775">"تمام"</string>
<string name="sim_added_title" msgid="3719670512889674693">"سیم کارت اضافه شد"</string>
<string name="sim_added_message" msgid="7797975656153714319">"برای دسترسی به شبکه تلفن همراه، دستگاهتان را مجدداً راهاندازی کنید."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"راهاندازی مجدد"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"تنظیم زمان"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"تاریخ تنظیم"</string>
<string name="date_time_set" msgid="5777075614321087758">"تنظیم"</string>
- <string name="date_time_done" msgid="2507683751759308828">"انجام شد"</string>
+ <string name="date_time_done" msgid="2507683751759308828">"تمام"</string>
<string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"جدید: "</font></string>
<string name="perms_description_app" msgid="5139836143293299417">"ارائه شده توسط <xliff:g id="APP_NAME">%1$s</xliff:g> ."</string>
<string name="no_permissions" msgid="7283357728219338112">"مجوزی لازم نیست"</string>
@@ -1085,7 +1085,7 @@
<string name="ime_action_search" msgid="658110271822807811">"جستجو"</string>
<string name="ime_action_send" msgid="2316166556349314424">"ارسال"</string>
<string name="ime_action_next" msgid="3138843904009813834">"بعدی"</string>
- <string name="ime_action_done" msgid="8971516117910934605">"انجام شد"</string>
+ <string name="ime_action_done" msgid="8971516117910934605">"تمام"</string>
<string name="ime_action_previous" msgid="1443550039250105948">"قبلی"</string>
<string name="ime_action_default" msgid="2840921885558045721">"اجرا کردن"</string>
<string name="dial_number_using" msgid="5789176425167573586">"شماره گیری \nبا استفاده از <xliff:g id="NUMBER">%s</xliff:g>"</string>
@@ -1131,7 +1131,7 @@
<item quantity="one"><xliff:g id="INDEX">%d</xliff:g> از <xliff:g id="TOTAL">%d</xliff:g></item>
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> از <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
- <string name="action_mode_done" msgid="7217581640461922289">"انجام شد"</string>
+ <string name="action_mode_done" msgid="7217581640461922289">"تمام"</string>
<string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"در حال پاک کردن حافظهٔ USB..."</string>
<string name="progress_erasing" product="default" msgid="6596988875507043042">"در حال پاک کردن کارت SD..."</string>
<string name="share" msgid="1778686618230011964">"اشتراکگذاری"</string>
@@ -1173,7 +1173,7 @@
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"لغو"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
- <string name="keyboardview_keycode_done" msgid="1992571118466679775">"انجام شد"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"تمام"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"تغییر حالت"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
@@ -1416,7 +1416,7 @@
<string name="immersive_cling_title" msgid="8394201622932303336">"مشاهده در حالت تمام صفحه"</string>
<string name="immersive_cling_description" msgid="3482371193207536040">"برای خروج، انگشتتان را از بالای صفحه به پایین بکشید."</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"متوجه شدم"</string>
- <string name="done_label" msgid="2093726099505892398">"انجام شد"</string>
+ <string name="done_label" msgid="2093726099505892398">"تمام"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"لغزنده دایرهای ساعت"</string>
<string name="minute_picker_description" msgid="8606010966873791190">"لغزنده دایرهای دقیقه"</string>
<string name="select_hours" msgid="6043079511766008245">"انتخاب ساعت"</string>
@@ -1436,7 +1436,7 @@
<string name="package_installed_device_owner" msgid="8420696545959087545">"توسط سرپرستتان نصب شد"</string>
<string name="package_updated_device_owner" msgid="8856631322440187071">"توسط سرپرست شما بهروزرسانی شد"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"توسط سرپرستتان حذف شد"</string>
- <string name="battery_saver_description" msgid="1960431123816253034">"برای کمک به بهبود ماندگاری باتری، ذخیره کننده باتری عملکرد دستگاهتان را کاهش میدهد و لرزش، سرویسهای مبتنی بر مکان، و دسترسی به اکثر دادهها در پسزمینه را محدود میکند. ایمیل، پیامرسانی و برنامههای دیگری که به همگامسازی متکی هستند، تا زمانیکه آنها را باز نکنید نمیتوانند بهروز شوند.\n\nذخیره کننده باتری بهصورت خودکار در هنگام شارژ شدن دستگاه خاموش میشود."</string>
+ <string name="battery_saver_description" msgid="1960431123816253034">"برای کمک به بهبود عمر باتری، بهینهسازی باتری عملکرد دستگاهتان را کاهش میدهد و لرزش، سرویسهای مبتنی بر مکان، و دسترسی به اکثر دادهها در پسزمینه را محدود میکند. ایمیل، پیامرسانی و برنامههای دیگری که به همگامسازی وابستهاند، تا زمانیکه آنها را باز نکنید نمیتوانند بهروز شوند.\n\nبهینهسازی باتری بهصورت خودکار در هنگام شارژ شدن دستگاه خاموش میشود."</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">به مدت %1$d دقیقه (تا <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">به مدت %1$d دقیقه (تا <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 7b5ca29..c32add0 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acceder aos teus contactos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Localización"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"acceso á localización deste dispositivo"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"acceder á localización deste dispositivo"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder ao teu calendario"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 9392997..6281abf 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -229,8 +229,8 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"સંપર્કો"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"તમારા સંપર્કોને ઍક્સેસ કરો"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"સ્થાન"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરો"</string>
- <string name="permgrouplab_calendar" msgid="5863508437783683902">"કૅલેન્ડર"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની"</string>
+ <string name="permgrouplab_calendar" msgid="5863508437783683902">"કેલેન્ડર"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS સંદેશા મોકલો અને જોવાની"</string>
@@ -326,14 +326,14 @@
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"એપ્લિકેશનને ઇનકમિંગ અને આઉટગોઇંગ કૉલ્સ વિશેનાં ડેટા સહિત, તમારા ફોનના કૉલ લૉગને સંશોધિત કરવાની મંજૂરી આપે છે. દુર્ભાવનાપૂર્ણ એપ્લિકેશનો આનો ઉપયોગ તમારા કૉલ લૉગને કાઢી નાખવા અથવા સંશોધિત માટે કરી શકે છે."</string>
<string name="permlab_bodySensors" msgid="4871091374767171066">"બૉડી સેન્સર્સ (જેમ કે હાર્ટ રેટ મૉનિટર્સ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"એપ્લિકેશનને તમારી હૃદય ગતિ જેવી તમારી શારીરિક સ્થિતિને મૉનિટર કરતાં સેન્સર્સથી ડેટા ઍક્સેસ કરવાની મંજૂરી આપે છે."</string>
- <string name="permlab_readCalendar" msgid="5972727560257612398">"કૅલેન્ડર ઇવેન્ટ્સ વત્તા ગોપનીયતા માહિતી વાંચો"</string>
- <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કૅલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કૅલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
- <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કૅલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
- <string name="permlab_writeCalendar" msgid="8438874755193825647">"કૅલેન્ડર ઇવેન્ટ્સ ઉમેરો અથવા સંશોધિત કરો અને માલિકની જાણ બહાર અતિથિઓને ઇમેઇલ મોકલો"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કૅલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કૅલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કૅલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
+ <string name="permlab_readCalendar" msgid="5972727560257612398">"કેલેન્ડર ઇવેન્ટ્સ વત્તા ગોપનીયતા માહિતી વાંચો"</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર સંગ્રહિત તમામ કેલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર સંગ્રહિત તમામ કેલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
+ <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર સંગ્રહિત તમામ કેલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
+ <string name="permlab_writeCalendar" msgid="8438874755193825647">"કેલેન્ડર ઇવેન્ટ્સ ઉમેરો અથવા સંશોધિત કરો અને માલિકની જાણ બહાર અતિથિઓને ઇમેઇલ મોકલો"</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરો"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"એપ્લિકેશનને વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને GPS અથવા અન્ય સ્થાન સ્રોતોના ઓપરેશનમાં દખલ કરવાની મંજૂરી આપી શકે છે."</string>
<string name="permlab_accessFineLocation" msgid="1191898061965273372">"નિશ્ચિત સ્થાન (GPS અને નેટવર્ક-આધારિત)"</string>
diff --git a/core/res/res/values-it-watch/strings.xml b/core/res/res/values-it-watch/strings.xml
index c348e48..aa6a4be 100644
--- a/core/res/res/values-it-watch/strings.xml
+++ b/core/res/res/values-it-watch/strings.xml
@@ -29,7 +29,7 @@
<string name="permgrouplab_storagewear" msgid="1003807594193602313">"accedere a foto, contenuti multimediali e file sull\'orologio"</string>
<string name="permgrouplab_microphonewear" msgid="1047561180980891136">"registrare audio"</string>
<string name="permgrouplab_camerawear" msgid="4543951283103407017">"scattare foto e registrare video"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"fare e gestire telefonate"</string>
+ <string name="permgrouplab_phonewear" msgid="134365036753766126">"eseguire e gestire le telefonate"</string>
<string name="permgrouplab_sensorswear" msgid="1429324744329327663">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
<string name="permlab_statusBarServicewear" msgid="2469402818964691034">"avere la funzione di barra di stato"</string>
<string name="permlab_bodySensorswear" msgid="7857941041202791873">"accedere ai sensori (come il cardiofrequenzimetro)"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4271287..72b1c54 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -239,11 +239,11 @@
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfono"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"registrare audio"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Fotocamera"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"acquisire foto e registrare video"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"scattare foto e registrare video"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefono"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"eseguire e gestire le telefonate"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Sensori per il corpo"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accedere ai dati dei sensori sui tuoi parametri vitali"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperare contenuti della finestra"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Esaminare i contenuti di una finestra con cui interagisci."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Attivare Esplora al tocco"</string>
@@ -272,7 +272,7 @@
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Consente all\'applicazione di leggere i messaggi cell broadcast ricevuti dal dispositivo. Gli avvisi cell broadcast vengono trasmessi in alcune località per avvertire di eventuali situazioni di emergenza. Le applicazioni dannose potrebbero interferire con il rendimento o con il funzionamento del dispositivo quando si riceve un messaggio cell broadcast di emergenza."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lettura feed sottoscritti"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Consente all\'applicazione di ottenere dettagli sui feed attualmente sincronizzati."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"invio e lettura di SMS"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"inviare e visualizzare SMS"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Consente all\'applicazione di inviare messaggi SMS. Ciò potrebbe comportare costi imprevisti. Applicazioni dannose potrebbero generare dei costi inviando messaggi senza la tua conferma."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"lettura messaggi di testo personali (SMS o MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Consente all\'applicazione di leggere i messaggi SMS memorizzati sul tablet o sulla scheda SIM. Ciò consente all\'applicazione di leggere tutti i messaggi SMS, indipendentemente dai contenuti o dal livello di riservatezza."</string>
@@ -342,7 +342,7 @@
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Consente all\'applicazione di ottenere la tua posizione approssimativa. Questa posizione viene ottenuta da servizi di localizzazione utilizzando fonti di geolocalizzazione delle reti come ripetitori di telefonia mobile e Wi-Fi. Questi servizi di localizzazione devono essere attivi e disponibili sul dispositivo per poter essere utilizzati dall\'applicazione. Le applicazioni potrebbero utilizzare questa autorizzazione per stabilire la tua posizione approssimativa."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifica impostazioni audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Consente all\'applicazione di modificare le impostazioni audio globali, come il volume e quale altoparlante viene utilizzato per l\'uscita."</string>
- <string name="permlab_recordAudio" msgid="3876049771427466323">"registrazione audio"</string>
+ <string name="permlab_recordAudio" msgid="3876049771427466323">"registrare audio"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Consente all\'applicazione di registrare audio con il microfono. Questa autorizzazione consente all\'applicazione di registrare audio in qualsiasi momento senza la tua conferma."</string>
<string name="permlab_sim_communication" msgid="1180265879464893029">"comunicazione SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Consente all\'app di inviare comandi alla SIM. Questo è molto pericoloso."</string>
diff --git a/core/res/res/values-ja-watch/strings.xml b/core/res/res/values-ja-watch/strings.xml
index 3248041..82c3ffd 100644
--- a/core/res/res/values-ja-watch/strings.xml
+++ b/core/res/res/values-ja-watch/strings.xml
@@ -37,7 +37,7 @@
<string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"おおよその位置情報(ネットワーク基地局)へのアクセス"</string>
<string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIMへのコマンド送信"</string>
<string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ネットワークへのフルアクセス"</string>
- <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"プロフィールの所有者と端末の所有者の管理"</string>
+ <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"プロファイルの所有者と端末の所有者の管理"</string>
<string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX状態の変更"</string>
<string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Androidビーム転送のステータスの受信"</string>
<string name="permlab_route_media_outputwear" msgid="8737024341474587192">"メディア出力のルーティング"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 545b963..135c7ea 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -282,8 +282,8 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAPメッセージの受信と処理をアプリに許可します。これにより、アプリが端末に届いたメッセージを表示することなく監視または削除できるようになります。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"実行中のアプリの取得"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"現在実行中または最近実行したタスクに関する情報の取得をアプリに許可します。これにより、その端末でどのアプリを使用しているかをアプリから識別できるようになる可能性があります。"</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"プロフィールの所有者と端末の所有者の管理"</string>
- <string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"プロフィールの所有者と端末の所有者の設定をアプリに許可します。"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"プロファイルの所有者と端末の所有者の管理"</string>
+ <string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"プロファイルの所有者と端末の所有者の設定をアプリに許可します。"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"実行中のアプリの順序変更"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"タスクをフォアグラウンドやバックグラウンドに移動することをアプリに許可します。これにより、アプリがユーザーからの入力なしでこの処理を実行する可能性があります。"</string>
<string name="permlab_enableCarMode" msgid="5684504058192921098">"運転モードの有効化"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index d39ee05..7af4df0 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контактілер"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"контактілерге кіру"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Орын"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"бұл құрылғының орнына кіру"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"бұл құрылғының орналасқан жерін көру"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнтізбе"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"күнтізбеге кіру"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index e7e43f0..f20551d 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Байланыштар"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"байланыштарыңызга уруксат"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Жайгашкан жер"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"бул түзмөктүн жайгашкан жерине кирүү"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"бул түзмөктүн жайгашкан жери тууралуу дайындарды көрүү"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнбарак"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"жылнаамаңызды пайдалануу"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-mcc240-mnc01/config.xml b/core/res/res/values-mcc240-mnc01/config.xml
new file mode 100644
index 0000000..7fb5c46
--- /dev/null
+++ b/core/res/res/values-mcc240-mnc01/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Do not set the system language as value of EF LI/EF PL -->
+ <bool name="config_use_sim_language_file">false</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc240-mnc05/config.xml b/core/res/res/values-mcc240-mnc05/config.xml
new file mode 100644
index 0000000..7fb5c46
--- /dev/null
+++ b/core/res/res/values-mcc240-mnc05/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Do not set the system language as value of EF LI/EF PL -->
+ <bool name="config_use_sim_language_file">false</bool>
+
+</resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index f10fb19..a41be56 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -227,23 +227,23 @@
<string name="user_owner_label" msgid="2804351898001038951">"Лични"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"Работа"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"пристапи до контактите"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"пристапува до контактите"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"да пристапува до локацијата на овој уред"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"пристапува до локацијата на овој уред"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"пристапи до календарот"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"пристапува до календарот"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"СМС"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"испраќа и прикажува СМС-пораки"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Меморија"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"пристапува до фотографии, медиуми и датотеки на уредот"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"пристапува до фотографии, аудио-видео и датотеки на уредот"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снимај аудио"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снима аудио"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Фотоапарат"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"фотографирај и снимај видео"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"фотографира и снима видео"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Телефон"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"повикувај и управувај со телефонски повици"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"упатува и управува со телефонски повици"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Телесни сензори"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"пристапи до податоците од сензорите за виталните знаци"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"пристапува до податоците од сензорите за виталните знаци"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Врати содржина на прозорец"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Провери ја содржината на прозорецот со кој се комуницира."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Вклучи „Истражувај со допир“"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index a547ca7..41528d8 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"အဆက်အသွယ်များ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"သင့် အဆက်အသွယ်များအား ဝင်ရောက်သုံးရန်"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"တည်နေရာ"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"စက်ပစ္စည်း၏ တည်နေရာကို အသုံးပြုမည်"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"ဤစက်ပစ္စည်း၏ တည်နေရာကို ရယူ"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ပြက္ခဒိန်"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"သင့်ပြက္ခဒိန်အား ဝင်ရောက်သုံးရန်"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"စာတိုစနစ်"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d203a6a..2f6c8bf 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -227,23 +227,23 @@
<string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatos"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acessar seus contatos"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acesse seus contatos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acessar sua agenda"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"enviar e ver mensagens SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do seu dispositivo"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do dispositivo"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"grave áudio"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefone"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"fazer e gerenciar chamadas telefônicas"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"faça e gerencie chamadas telefônicas"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporais"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acessar dados do sensor sobre seus sinais vitais"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acesse dados do sensor sobre seus sinais vitais"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar cont. da janela"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecionar o conteúdo da janela com que você está interagindo."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Ativar Explorar por toque"</string>
@@ -272,7 +272,7 @@
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds inscritos"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite que o app obtenha detalhes sobre os feeds sincronizados no momento."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"enviar e ver mensagens SMS"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"envie e veja mensagens SMS"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que o app envie mensagens SMS. Isso pode resultar em cobranças inesperadas. Apps maliciosos podem gerar custos através do envio de mensagens sem sua confirmação."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"ler suas mensagens de texto (SMS ou MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que o app leia mensagens SMS armazenadas no tablet ou cartão SIM. Isso permite que o app leia todas as mensagens SMS, independentemente de seu conteúdo ou confidencialidade."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d203a6a..2f6c8bf 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -227,23 +227,23 @@
<string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatos"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acessar seus contatos"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acesse seus contatos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acessar sua agenda"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"enviar e ver mensagens SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do seu dispositivo"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do dispositivo"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"grave áudio"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefone"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"fazer e gerenciar chamadas telefônicas"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"faça e gerencie chamadas telefônicas"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporais"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acessar dados do sensor sobre seus sinais vitais"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acesse dados do sensor sobre seus sinais vitais"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar cont. da janela"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecionar o conteúdo da janela com que você está interagindo."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Ativar Explorar por toque"</string>
@@ -272,7 +272,7 @@
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds inscritos"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite que o app obtenha detalhes sobre os feeds sincronizados no momento."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"enviar e ver mensagens SMS"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"envie e veja mensagens SMS"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que o app envie mensagens SMS. Isso pode resultar em cobranças inesperadas. Apps maliciosos podem gerar custos através do envio de mensagens sem sua confirmação."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"ler suas mensagens de texto (SMS ou MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que o app leia mensagens SMS armazenadas no tablet ou cartão SIM. Isso permite que o app leia todas as mensagens SMS, independentemente de seu conteúdo ou confidencialidade."</string>
diff --git a/core/res/res/values-ro-watch/strings.xml b/core/res/res/values-ro-watch/strings.xml
index e412cad..9a8e4e0 100644
--- a/core/res/res/values-ro-watch/strings.xml
+++ b/core/res/res/values-ro-watch/strings.xml
@@ -22,15 +22,15 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplic. <xliff:g id="NUMBER_0">%1$d</xliff:g> din <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Senzori"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"să acceseze persoanele de contact"</string>
+ <string name="permgrouplab_contactswear" msgid="2340286500790908344">"acceseze persoanele de contact"</string>
<string name="permgrouplab_locationwear" msgid="6275317222482780209">"să acceseze locația acestui ceas"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"să acceseze calendarul"</string>
- <string name="permgrouplab_smswear" msgid="6849506550342974220">"să trimită și să vadă mesajele SMS"</string>
+ <string name="permgrouplab_calendarwear" msgid="441900844045065081">"acceseze calendarul"</string>
+ <string name="permgrouplab_smswear" msgid="6849506550342974220">"trimită și să vadă mesajele SMS"</string>
<string name="permgrouplab_storagewear" msgid="1003807594193602313">"să acceseze fotografiile, conținutul media și fișierele de pe ceas"</string>
- <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"să înregistreze conținut audio"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"să fotografieze și să înregistreze videoclipuri"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"să inițieze să și gestioneze apeluri telefonice"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"să acceseze datele de la senzori despre semnele vitale"</string>
+ <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"înregistreze sunet"</string>
+ <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fotografieze și să înregistreze imagini"</string>
+ <string name="permgrouplab_phonewear" msgid="134365036753766126">"inițieze să și gestioneze apeluri telefonice"</string>
+ <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"acceseze datele de la senzori despre semnele vitale"</string>
<string name="permlab_statusBarServicewear" msgid="2469402818964691034">"să fie bara de stare"</string>
<string name="permlab_bodySensorswear" msgid="7857941041202791873">"să acceseze senzorii corporali (cum ar fi monitoarele cardiace)"</string>
<string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"să acceseze locația exactă (bazată pe GPS și pe rețea)"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d215d4c..5062e76 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -135,7 +135,7 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Se preferă conexiunea Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="5920549484600758786">"Se preferă conexiunea mobilă"</string>
<string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Numai Wi-Fi"</string>
- <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecţionată"</string>
+ <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecționată"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> după <xliff:g id="TIME_DELAY">{2}</xliff:g> (de) secunde"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecţionat"</string>
@@ -232,19 +232,19 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Locație"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acceseze locația acestui dispozitiv"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"accesează calendarul"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceseze calendarul"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"trimite și vede mesajele SMS"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"trimită și să vadă mesajele SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Stocare"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"acceseze fotografiile, conținutul media și fișierele de pe dispozitiv"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfonul"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"înregistreze conținut audio"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"înregistreze sunet"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Camera foto"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotografieze și să înregistreze videoclipuri"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotografieze și să înregistreze imagini"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefon"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"inițieze și să gestioneze apeluri telefonice"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Senzori corporali"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accesează datele înregistrate de senzori despre semnele vitale"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acceseze datele de la senzori despre semnele vitale"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperează conținutul ferestrei"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspectează conținutul unei ferestre cu care interacționați."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Activează funcția Explorați prin atingere"</string>
@@ -270,11 +270,11 @@
<string name="permlab_receiveMms" msgid="1821317344668257098">"primeşte mesaje text (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"citeşte mesajele cu transmisie celulară"</string>
- <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situaţiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanţa sau funcţionarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanţa sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"citire feeduri abonat"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite aplicației să obţină detalii despre feedurile sincronizate în prezent."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"trimite și vede mesajele SMS"</string>
- <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea determina apariţia unor taxe neaşteptate. Aplicaţiile rău intenţionate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"trimită și să vadă mesajele SMS"</string>
+ <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea determina apariția unor taxe neaşteptate. Aplicaţiile rău intenţionate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"citeşte mesajele text (SMS sau MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite aplicației să citească mesajele SMS stocate pe tabletă sau pe cardul SIM. În acest fel, aplicația poate citi toate mesajele SMS, indiferent de conţinutul sau de gradul de confidenţialitate al acestora."</string>
<string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Permite aplicației să citească mesajele SMS stocate pe televizor sau pe cardul SIM. Cu această permisiune, aplicația poate citi toate mesajele SMS, indiferent de conținutul sau de gradul de confidențialitate al acestora."</string>
@@ -292,11 +292,11 @@
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"închide alte aplicații"</string>
<string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Permite aplicației să oprească procesele derulate în fundal de alte aplicații. Acest lucru poate face ca respectivele aplicații să nu mai ruleze."</string>
<string name="permlab_systemAlertWindow" msgid="3543347980839518613">"suprapune elemente vizuale peste alte aplicații"</string>
- <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permite aplicației să suprapună elemente vizuale peste alte aplicații sau părţi ale interfeţei cu utilizatorul. Acestea pot interfera cu utilizarea de către dvs. a interfeţei în orice aplicație sau pot schimba ceea ce credeţi că vedeţi în alte aplicații."</string>
+ <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permite aplicației să suprapună elemente vizuale peste alte aplicații sau părți ale interfeței cu utilizatorul. Acestea pot interfera cu utilizarea de către dvs. a interfeței în orice aplicație sau pot schimba ceea ce credeți că vedeţi în alte aplicații."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"rulare continuă a aplicației"</string>
- <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Permite aplicației să declare persistente în memorie anumite părţi ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcţionarea tabletei."</string>
+ <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Permite aplicației să declare persistente în memorie anumite părți ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcționarea tabletei."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Permite aplicației să declare persistente în memorie anumite părți ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcționarea televizorului."</string>
- <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Permite aplicației să declare persistente în memorie anumite părţi ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcţionarea telefonului."</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Permite aplicației să declare persistente în memorie anumite părți ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcționarea telefonului."</string>
<string name="permlab_getPackageSize" msgid="7472921768357981986">"măsurare spaţiu de stocare al aplicației"</string>
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"Permite aplicației să preia dimensiunile codului, ale datelor și ale memoriei cache"</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"modifică setări de sistem"</string>
@@ -310,17 +310,17 @@
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Permite aplicației să trimită mesaje difuzate persistente, care se păstrează după terminarea difuzării mesajului. Utilizarea excesivă a acestei funcții poate să încetinească sau să destabilizeze televizorul, determinându-l să utilizeze prea multă memorie."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Permite aplicației să trimită mesaje difuzate persistente, care se păstrează după terminarea difuzării mesajului. Utilizarea excesivă a acestei funcții poate să încetinească sau să destabilizeze telefonul, determinându-l să utilizeze prea multă memorie."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"citeşte agenda"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Permite aplicației să citească datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără ştirea dvs."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Permite aplicației să citească datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără știrea dvs."</string>
<string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Permite aplicației să citească datele despre persoanele de contact salvate pe televizor, inclusiv frecvența cu care ați apelat, ați trimis e-mailuri sau ați comunicat în alte moduri cu anumite persoane. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău-intenționate pot permite accesul la datele de contact fără cunoștința dvs."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Permite aplicației să citească datele despre persoanele din agenda stocată pe telefon, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără ştirea dvs."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Permite aplicației să citească datele despre persoanele din agenda stocată pe telefon, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără știrea dvs."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"modifică agenda"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
<string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Permite aplicației să modifice datele despre persoanele de contact salvate pe televizor, inclusiv frecvența cu care ați apelat, ați trimis e-mailuri sau ați comunicat în alte moduri cu anumite persoane de contact. Cu această permisiune, aplicația poate șterge datele de contact."</string>
- <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe telefon, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe telefon, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"citeşte jurnalul de apeluri"</string>
- <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Permite aplicației să citească jurnalul de apeluri al tabletei, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără ştirea dvs."</string>
+ <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Permite aplicației să citească jurnalul de apeluri al tabletei, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără știrea dvs."</string>
<string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Permite aplicației să citească jurnalul de apeluri al televizorului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune, aplicațiile pot să salveze datele din jurnalul de apeluri, iar aplicațiile rău-intenționate pot permite accesul la datele din jurnalul de apeluri fără cunoștința dvs."</string>
- <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Permite aplicației să citească jurnalul de apeluri al telefonului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără ştirea dvs."</string>
+ <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Permite aplicației să citească jurnalul de apeluri al telefonului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără știrea dvs."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"scrie jurnalul de apeluri"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite aplicației să modifice jurnalul de apeluri al tabletei dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite aplicației să modifice jurnalul de apeluri al televizorului, inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău-intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul de apeluri."</string>
@@ -331,10 +331,10 @@
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite aplicației să citească toate evenimentele din calendar stocate pe tabletă, inclusiv cele ale prietenilor sau colegilor. Acest lucru poate permite aplicației să distribuie sau să salveze datele din calendar, indiferent dacă acestea sunt confidenţiale sau sensibile."</string>
<string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Permite aplicației să citească toate evenimentele din calendar stocate pe televizor, inclusiv cele ale prietenilor sau colegilor. Cu această permisiune, aplicația poate să permită accesul la datele din calendar sau să le salveze, indiferent dacă acestea sunt confidențiale sau sensibile."</string>
<string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Permite aplicației să citească toate evenimentele din calendar stocate pe telefon, inclusiv cele ale prietenilor sau colegilor. Acest lucru poate permite aplicației să distribuie sau să salveze datele din calendar, indiferent dacă acestea sunt confidenţiale sau sensibile."</string>
- <string name="permlab_writeCalendar" msgid="8438874755193825647">"adăugarea sau modificarea evenimentelor din calendar și trimiterea de e-mailuri invitaţilor fără ştirea proprietarului"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe tabletă, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără ştirea proprietarilor."</string>
+ <string name="permlab_writeCalendar" msgid="8438874755193825647">"adăugarea sau modificarea evenimentelor din calendar și trimiterea de e-mailuri invitaților fără știrea proprietarului"</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe tabletă, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără știrea proprietarilor."</string>
<string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe televizor, inclusiv pe cele ale prietenilor sau ale colegilor. Cu această permisiune, aplicația poate să trimită mesaje care par că vin din partea proprietarilor calendarului sau să modifice evenimentele fără cunoștința acestora."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe telefon, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără ştirea proprietarilor."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe telefon, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără știrea proprietarilor."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesare comenzi suplimentare ale furnizorului locației"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite aplicației să acceseze comenzi suplimentare pentru furnizorul locației. Aplicația ar putea să utilizeze această permisiune pentru a influența operațiile GPS sau ale altor surse de locații."</string>
<string name="permlab_accessFineLocation" msgid="1191898061965273372">"locaţia exactă (bazată pe rețea și GPS)"</string>
@@ -343,7 +343,7 @@
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite aplicației să obţină locaţia dvs. aproximativă. Această locație este dedusă de serviciile de localizare utilizând surse de localizare prin rețele, cum ar fi cele prin turn de celule și Wi-Fi. Pentru a fi utilizate de aplicație, aceste servicii de localizare trebuie să fie activate și disponibile pe dispozitivul dvs. Aplicaţiile pot utiliza această permisiune pentru a determina locaţia dvs. aproximativă."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modificare setări audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite aplicației să modifice setările audio globale, cum ar fi volumul și difuzorul care este utilizat pentru ieșire."</string>
- <string name="permlab_recordAudio" msgid="3876049771427466323">"înregistrare audio"</string>
+ <string name="permlab_recordAudio" msgid="3876049771427466323">"înregistreze sunet"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite aplicației să efectueze înregistrări audio cu ajutorul microfonului. Cu această permisiune aplicația efectuează oricând înregistrări audio fără confirmare."</string>
<string name="permlab_sim_communication" msgid="1180265879464893029">"comunicare cu cardul SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite aplicației să trimită comenzi pe cardul SIM. Această permisiune este foarte periculoasă."</string>
@@ -354,11 +354,11 @@
<string name="permlab_flashlight" msgid="2155920810121984215">"control lanternă"</string>
<string name="permdesc_flashlight" msgid="6522284794568368310">"Permite aplicației să controleze lanterna."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"apelare directă numere de telefon"</string>
- <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite aplicației să apeleze numere de telefon fără intervenţia dvs. Acest lucru poate determina apariţia unor taxe sau a unor apeluri neaşteptate. Cu această permisiune aplicația nu poate apela numerele de urgenţă. Aplicaţiile rău intenţionate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string>
+ <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite aplicației să apeleze numere de telefon fără intervenţia dvs. Acest lucru poate determina apariția unor taxe sau a unor apeluri neaşteptate. Cu această permisiune aplicația nu poate apela numerele de urgenţă. Aplicaţiile rău intenţionate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string>
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"accesează serviciul de apelare IMS"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția dvs."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"citeşte starea și identitatea telefonului"</string>
- <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabileşte numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanţă conectat printr-un apel."</string>
+ <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanţă conectat printr-un apel."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"împiedicarea computerului tablet PC să intre în repaus"</string>
<string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"împiedică intrarea televizorului în stare de inactivitate"</string>
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"împiedicare intrare telefon în repaus"</string>
@@ -756,7 +756,7 @@
<string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Rămâneți în această pagină"</string>
<string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nSigur doriți să părăsiți această pagină?"</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirmați"</string>
- <string name="double_tap_toast" msgid="4595046515400268881">"Sfat: măriţi și micşoraţi prin dublă atingere."</string>
+ <string name="double_tap_toast" msgid="4595046515400268881">"Sfat: măriți și micșorați prin dublă atingere."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Automat"</string>
<string name="setup_autofill" msgid="7103495070180590814">"Conf.Compl.auto."</string>
<string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string>
@@ -767,7 +767,7 @@
<string name="autofill_postal_code" msgid="4696430407689377108">"Cod poştal"</string>
<string name="autofill_state" msgid="6988894195520044613">"Stat"</string>
<string name="autofill_zip_code" msgid="8697544592627322946">"Cod ZIP"</string>
- <string name="autofill_county" msgid="237073771020362891">"Judeţ"</string>
+ <string name="autofill_county" msgid="237073771020362891">"Județ"</string>
<string name="autofill_island" msgid="4020100875984667025">"Insulă"</string>
<string name="autofill_district" msgid="8400735073392267672">"District"</string>
<string name="autofill_department" msgid="5343279462564453309">"Departament"</string>
@@ -776,11 +776,11 @@
<string name="autofill_area" msgid="3547409050889952423">"Zonă"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"Emirat"</string>
<string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"citeşte marcajele și istoricul web"</string>
- <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Permite aplicației să citească istoricul tuturor adreselor URL accesate de Browser și toate marcajele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Permite aplicației să citească istoricul tuturor adreselor URL accesate de Browser și toate marcajele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacități de navigare pe web."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"scrie în marcajele și în istoricul web"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe tabletă. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe tabletă. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacități de navigare pe web."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Permite aplicației să modifice istoricul sau marcajele browserului stocate pe televizor. Cu această permisiune, aplicația poate șterge sau modifica datele din browser. Notă: această permisiune nu poate fi aplicată de browsere terță parte sau de alte aplicații cu capacități de navigare pe web."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe telefon. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe telefon. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacități de navigare pe web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"setează o alarmă"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite aplicației să seteze o alarmă într-o aplicație de ceas cu alarmă instalată. Este posibil ca unele aplicații de ceas cu alarmă să nu implementeze această funcție."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"adăugare mesagerie vocală"</string>
@@ -803,11 +803,11 @@
<string name="searchview_description_search" msgid="6749826639098512120">"Căutați"</string>
<string name="searchview_description_query" msgid="5911778593125355124">"Interogare de căutare"</string>
<string name="searchview_description_clear" msgid="1330281990951833033">"Ștergeți interogarea"</string>
- <string name="searchview_description_submit" msgid="2688450133297983542">"Trimiteţi interogarea"</string>
+ <string name="searchview_description_submit" msgid="2688450133297983542">"Trimiteți interogarea"</string>
<string name="searchview_description_voice" msgid="2453203695674994440">"Căutare vocală"</string>
- <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Activați Exploraţi prin atingere?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Exploraţi prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu tableta."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Exploraţi prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu telefonul."</string>
+ <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Activați Explorați prin atingere?"</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu tableta."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu telefonul."</string>
<string name="oneMonthDurationPast" msgid="7396384508953779925">"cu 1 lună în urmă"</string>
<string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Cu mai mult de 1 lună în urmă"</string>
<plurals name="last_num_days" formatted="false" msgid="5104533550723932025">
@@ -918,7 +918,7 @@
<string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g> a fost lansată iniţial."</string>
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scară"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Afişaţi întotdeauna"</string>
- <string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reactivaţi acest mod din Setări de sistem > Aplicații > Descărcate."</string>
+ <string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reactivați acest mod din Setări de sistem > Aplicații > Descărcate."</string>
<string name="smv_application" msgid="3307209192155442829">"Aplicaţia <xliff:g id="APPLICATION">%1$s</xliff:g> (procesul <xliff:g id="PROCESS">%2$s</xliff:g>) a încălcat propria politică StrictMode."</string>
<string name="smv_process" msgid="5120397012047462446">"Procesul <xliff:g id="PROCESS">%1$s</xliff:g> a încălcat propria politică StrictMode."</string>
<string name="android_upgrading_title" msgid="1584192285441405746">"Android trece la o versiune superioară..."</string>
@@ -1002,10 +1002,10 @@
<string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> trimite un număr mare de mesaje SMS. Permiteți acestei aplicații să trimită în continuare mesaje?"</string>
<string name="sms_control_yes" msgid="3663725993855816807">"Permiteți"</string>
<string name="sms_control_no" msgid="625438561395534982">"Refuzaţi"</string>
- <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> intenţionează să trimită un mesaj la <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string>
+ <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> intenționează să trimită un mesaj la <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string>
<string name="sms_short_code_details" msgid="5873295990846059400">"Acest lucru "<b>"poate genera costuri"</b>" în contul dvs. mobil."</string>
<string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Acest lucru va genera costuri în contul dvs. mobil."</b></string>
- <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Trimiteţi"</string>
+ <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Trimiteți"</string>
<string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"Anulați"</string>
<string name="sms_short_code_remember_choice" msgid="5289538592272218136">"Doresc să se reţină opţiunea"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"Puteți modifica ulterior în Setări > Aplicații"</string>
@@ -1080,8 +1080,8 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Se formatează…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Nu este introdus"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Nu s-a găsit nicio activitate potrivită."</string>
- <string name="permlab_route_media_output" msgid="1642024455750414694">"Direcţionează rezultatele media"</string>
- <string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite unei aplicații să direcţioneze rezultate media către alte dispozitive externe."</string>
+ <string name="permlab_route_media_output" msgid="1642024455750414694">"Direcționează rezultatele media"</string>
+ <string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite unei aplicații să direcționeze rezultate media către alte dispozitive externe."</string>
<string name="permlab_readInstallSessions" msgid="6165432407628065939">"Citirea sesiunilor de instalare"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite unei aplicații accesul la citirea sesiunilor de instalare. Aceasta poate vedea detalii despre instalările de pachete active."</string>
<string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicită instalarea pachetelor"</string>
@@ -1124,7 +1124,7 @@
<string name="upload_file" msgid="2897957172366730416">"Alegeţi un fişier"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nu au fost găsite fișiere"</string>
<string name="reset" msgid="2448168080964209908">"Resetaţi"</string>
- <string name="submit" msgid="1602335572089911941">"Trimiteţi"</string>
+ <string name="submit" msgid="1602335572089911941">"Trimiteți"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Mod Maşină activat"</string>
<string name="car_mode_disable_notification_message" msgid="8035230537563503262">"Atingeți pentru a ieşi din modul Maşină."</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"Tethering sau hotspot activ"</string>
@@ -1160,21 +1160,21 @@
<string name="choose_account_label" msgid="5655203089746423927">"Alegeţi un cont"</string>
<string name="add_account_label" msgid="2935267344849993553">"Adăugaţi un cont"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"Adăugaţi un cont"</string>
- <string name="number_picker_increment_button" msgid="2412072272832284313">"Creşteţi"</string>
+ <string name="number_picker_increment_button" msgid="2412072272832284313">"Creșteți"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"Reduceţi"</string>
<string name="number_picker_increment_scroll_mode" msgid="3073101067441638428">"Atingeți și țineți apăsat <xliff:g id="VALUE">%s</xliff:g>."</string>
<string name="number_picker_increment_scroll_action" msgid="9101473045891835490">"Glisaţi în sus pentru a creşte și în jos pentru a reduce."</string>
- <string name="time_picker_increment_minute_button" msgid="8865885114028614321">"Creşteţi valoarea pentru minute"</string>
+ <string name="time_picker_increment_minute_button" msgid="8865885114028614321">"Creșteți valoarea pentru minute"</string>
<string name="time_picker_decrement_minute_button" msgid="6246834937080684791">"Reduceţi valoarea pentru minute"</string>
- <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"Creşteţi valoarea pentru oră"</string>
+ <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"Creșteți valoarea pentru oră"</string>
<string name="time_picker_decrement_hour_button" msgid="1377479863429214792">"Reduceţi valoarea pentru oră"</string>
<string name="time_picker_increment_set_pm_button" msgid="4147590696151230863">"Setaţi valoarea PM"</string>
<string name="time_picker_decrement_set_am_button" msgid="8302140353539486752">"Setaţi valoarea AM"</string>
- <string name="date_picker_increment_month_button" msgid="5369998479067934110">"Creşteţi valoarea pentru lună"</string>
+ <string name="date_picker_increment_month_button" msgid="5369998479067934110">"Creșteți valoarea pentru lună"</string>
<string name="date_picker_decrement_month_button" msgid="1832698995541726019">"Reduceţi valoarea pentru lună"</string>
- <string name="date_picker_increment_day_button" msgid="7130465412308173903">"Creşteţi valoarea pentru zi"</string>
+ <string name="date_picker_increment_day_button" msgid="7130465412308173903">"Creșteți valoarea pentru zi"</string>
<string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduceţi valoarea pentru zi"</string>
- <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creşteţi valoarea pentru an"</string>
+ <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creșteți valoarea pentru an"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceţi valoarea pentru an"</string>
<string name="date_picker_prev_month_button" msgid="2858244643992056505">"Luna trecută"</string>
<string name="date_picker_next_month_button" msgid="5559507736887605055">"Luna viitoare"</string>
@@ -1245,7 +1245,7 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tabletă"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Căşti"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Căști"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Difuz. dispozit. andocare"</string>
<string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f1deb22..af80ed3 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -915,7 +915,7 @@
<string name="anr_application_process" msgid="8941757607340481057">"Приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" не отвечает. Закрыть его?"</string>
<string name="anr_process" msgid="6513209874880517125">"Приложение \"<xliff:g id="PROCESS">%1$s</xliff:g>\" не отвечает.\n\nЗакрыть его?"</string>
<string name="force_close" msgid="8346072094521265605">"ОК"</string>
- <string name="report" msgid="4060218260984795706">"Отзыв"</string>
+ <string name="report" msgid="4060218260984795706">"Создать отчет"</string>
<string name="wait" msgid="7147118217226317732">"Подождать"</string>
<string name="webpage_unresponsive" msgid="3272758351138122503">"Страница не отвечает.\n\nЗакрыть ее?"</string>
<string name="launch_warning_title" msgid="1547997780506713581">"Приложение перенаправлено"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index d5bafd25..582f912 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"සම්බන්ධතා"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ඔබේ සම්බන්ධතාවලට පිවිසෙන්න"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ස්ථානය"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"මෙම උපාංගයේ ස්ථානය ප්රවේශ කිරීම"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"මෙම උපාංගයේ ස්ථානයට ප්රවේශ කරන්න"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"දින දර්ශනය"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"ඔබේ දින දර්ශනයට පිවිසෙන්න"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"කෙටි පණිවිඩ"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index cf87e2a..c5ca3c2 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -993,7 +993,7 @@
<string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct ni bilo mogoče zagnati."</string>
<string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct je vklopljen"</string>
<string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dotaknite se za nastavitve"</string>
- <string name="accept" msgid="1645267259272829559">"Sprejmi"</string>
+ <string name="accept" msgid="1645267259272829559">"Sprejmem"</string>
<string name="decline" msgid="2112225451706137894">"Zavrni"</string>
<string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Povabilo je poslano"</string>
<string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"Povabilo za povezavo"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 86f9d11..7df0f86 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktet"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"qasu te kontaktet e tua"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Vendndodhja"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"qasu te vendndodhja e kësaj pajisjeje"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"qasjen te vendndodhja e kësaj pajisjeje"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendari"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"qasu te kalendari yt"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index a3a4b32..5bb2d78 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -230,21 +230,21 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"приступи контактима"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"приступ локацији овог уређаја"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"приступи локацији овог уређаја"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"приступи календару"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"шаље и прегледа SMS поруке"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Складиште"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"приступи сликама, медијима и датотекама на уређају"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"приступа сликама, медијима и датотекама на уређају"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снимање аудио снимака"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снима аудио снимке"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"снимање слика и видео снимака"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"снима слике и видео снимке"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Телефон"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"упућивање телефонских позива и управљање њима"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"упућује телефонске позиве и управља њима"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Сензори за тело"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"приступ подацима сензора о виталним функцијама"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"приступа подацима сензора о виталним функцијама"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Преузима садржај прозора"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Проверава садржај прозора са којим остварујете интеракцију."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Укључи Истраживања додиром"</string>
@@ -273,7 +273,7 @@
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Омогућава апликацији да чита поруке инфо сервиса које уређај прима. Упозорења инфо сервиса се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на учинак или ометају функционисање уређаја када се прими порука инфо сервиса о хитном случају."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"читање пријављених фидова"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Дозвољава апликацији да преузима детаље о тренутно синхронизованим фидовима."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"шаљи и прегледај SMS поруке"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"шаље и прегледа SMS поруке"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Дозвољава апликацији да шаље SMS поруке. Ово може да доведе до неочекиваних трошкова. Злонамерне апликације могу да шаљу поруке без ваше потврде, што може да изазове трошкове."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"читање текстуалних порука (SMS или MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Дозвољава апликацији да чита SMS поруке ускладиштене на таблету или SIM картици. Ово омогућава апликацији да чита све SMS поруке, без обзира на садржај или поверљивост."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 237afe7..488470f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -853,7 +853,7 @@
<string name="Midnight" msgid="5630806906897892201">"Midnatt"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="6876518925844129331">"Välj alla"</string>
+ <string name="selectAll" msgid="6876518925844129331">"Markera allt"</string>
<string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
<string name="copy" msgid="2681946229533511987">"Kopiera"</string>
<string name="paste" msgid="5629880836805036433">"Klistra in"</string>
@@ -1190,7 +1190,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Fler alternativ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="4891916833657929263">"Internminne"</string>
+ <string name="storage_internal" msgid="4891916833657929263">"lagring"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kort (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-enhet"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 6445f6a..d367887 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -231,7 +231,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Anwani"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ifikie anwani zako"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Mahali"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"fikia mahali kilipo kifaa hiki"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"ifikie mahali kilipo kifaa hiki"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"fikia kalenda yako"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 37d9bac..bf046e3 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -350,8 +350,8 @@
<string name="permdesc_camera" msgid="8497216524735535009">"Uygulamaya kamerayla fotoğraf ve video çekme izni verir. Bu izin, uygulamanın sizin onayınız olmadan istediği zaman kamerayı kullanmasına olanak sağlar."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"titreşimi denetleme"</string>
<string name="permdesc_vibrate" msgid="6284989245902300945">"Uygulamaya, titreşimi denetleme izni verir."</string>
- <string name="permlab_flashlight" msgid="2155920810121984215">"flaşı denetle"</string>
- <string name="permdesc_flashlight" msgid="6522284794568368310">"Uygulamaya, flaş ışığını denetleme izni verir."</string>
+ <string name="permlab_flashlight" msgid="2155920810121984215">"el fenerini denetle"</string>
+ <string name="permdesc_flashlight" msgid="6522284794568368310">"Uygulamaya, el fenerini denetleme izni verir."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"telefon numaralarına doğrudan çağrı yap"</string>
<string name="permdesc_callPhone" msgid="3740797576113760827">"Uygulamaya sizin müdahaleniz olmadan telefon numaralarına çağrı yapma izni verir. Bu durum beklenmeyen ödemelere veya çağrılara neden olabilir. Ancak bu iznin, uygulamanın acil numaralara çağrı yapmasına olanak sağlamadığını unutmayın. Kötü amaçlı uygulamalar onayınız olmadan çağrılar yaparak sizi zarara sokabilir."</string>
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS çağrı hizmetine erişme"</string>
diff --git a/core/res/res/values-uz-rUZ-watch/strings.xml b/core/res/res/values-uz-rUZ-watch/strings.xml
index 0fe54a1..44af51d 100644
--- a/core/res/res/values-uz-rUZ-watch/strings.xml
+++ b/core/res/res/values-uz-rUZ-watch/strings.xml
@@ -22,15 +22,15 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>dan <xliff:g id="NUMBER_0">%1$d</xliff:g> ilova."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensorlar"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"kontaktlarga kirishga ruxsat berish"</string>
+ <string name="permgrouplab_contactswear" msgid="2340286500790908344">"kontaktlarga kirish"</string>
<string name="permgrouplab_locationwear" msgid="6275317222482780209">"mazkur soatning joylashgan joyini ko‘rishga ruxsat berish"</string>
<string name="permgrouplab_calendarwear" msgid="441900844045065081">"taqvim ma’lumotlariga kirish"</string>
<string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS xabarlarni yuborish va ko‘rish"</string>
<string name="permgrouplab_storagewear" msgid="1003807594193602313">"Soatingizdagi rasmlar, media va fayllarga kirish"</string>
<string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ovoz yozib olish"</string>
- <string name="permgrouplab_camerawear" msgid="4543951283103407017">"rasmga tushirish va videoga olish"</string>
+ <string name="permgrouplab_camerawear" msgid="4543951283103407017">"suratga olish va video yozib olish"</string>
<string name="permgrouplab_phonewear" msgid="134365036753766126">"telefon qo‘ng‘iroqlarini amalga oshirish va boshqarish"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"asosiy belgilaringiz haqidagi sezgich ma’lumotlaridan foydalanishga ruxsat"</string>
+ <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"organizm holati haqidagi sezgich ma’lumotlariga kirish"</string>
<string name="permlab_statusBarServicewear" msgid="2469402818964691034">"holat qatorida ko‘rinishi"</string>
<string name="permlab_bodySensorswear" msgid="7857941041202791873">"tana sezgichlari (m-n, yurak urishi sensori) ma’lumotlaridan foydalanishga ruxsat"</string>
<string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"aniq joylashuv (GPS va tarmoqqa asoslanib) ma’lumotlaridan foydalanishga ruxsat"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 325ad91..b7ae992 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -231,19 +231,19 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Joylashuv"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"qurilmaning joylashuvi haqidagi ma’lumotlarga kirish"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Taqvim"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvimga kirish"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvim ma’lumotlariga kirish"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS xabarlarni yuborish va ko‘rish"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Xotira"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"qurilmangizdagi rasm, media va fayllarga kirish"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"qurilmangizdagi rasm, multimedia va fayllarga kirish"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"ovoz yozib olish"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"rasm va videoga olish"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"suratga olish va video yozib olish"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefon"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"telefon qo‘ng‘iroqlarini amalga oshirish va boshqarish"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Tana sezgichlari"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"asosiy belgilaringiz haqidagi sezgich ma’lumotlariga kirish"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"organizm holati haqidagi sezgich ma’lumotlariga kirish"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Oynadagi kontentni o‘qiydi"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Joriy oynadagi kontent mazmunini aniqlaydi."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Teginib o‘rganish xizmatini yoqadi"</string>
@@ -342,7 +342,7 @@
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Ilovaga sizning taxminiy joylashuvingizni topishga ruxsat beradi. Ushbu joylashuv Wi-Fi va uyali tarmoq antennalari kabi tarmoq joylashuv manbalaridan foydlanuvchi joylashuv xizmatlari orqali aniqlanadi. Ushbu joylashuv xizmatlari yoqib qo‘yilgan bo‘lishi va qurilmangizdagi ilovaga ulardan foydalanish uchun mavjud bo‘lishi kerak. Ilovalar bundan foydalanib, sizning taxminiy joylashuvingizni aniqlaydi."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio sozlamalaringizni o‘zgartirish"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ilovalarga tovush va ovoz chiqarish uchun foydalaniladigan karnay kabi global audio sozlamalarini o‘zgartirish uchun ruxsat beradi."</string>
- <string name="permlab_recordAudio" msgid="3876049771427466323">"audioni yozib olish"</string>
+ <string name="permlab_recordAudio" msgid="3876049771427466323">"ovoz yozib olish"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Ilovaga mikrofon yordamida audio yozish uchun ruxsat beradi. Bu huquq ilovaga ruxsatingizsiz audio fayllarni yozib olishga ruxsat beradi."</string>
<string name="permlab_sim_communication" msgid="1180265879464893029">"sim orqali ulanish"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Dasturga SIM kartaga buyruqlar jo‘natishga ruxsat beradi. Bu juda ham xavfli."</string>
@@ -797,9 +797,9 @@
<string name="menu_space_shortcut_label" msgid="2410328639272162537">"space"</string>
<string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
<string name="menu_delete_shortcut_label" msgid="3658178007202748164">"delete"</string>
- <string name="search_go" msgid="8298016669822141719">"Izlash"</string>
+ <string name="search_go" msgid="8298016669822141719">"Qidirish"</string>
<string name="search_hint" msgid="1733947260773056054">"Qidirish…"</string>
- <string name="searchview_description_search" msgid="6749826639098512120">"Izlash"</string>
+ <string name="searchview_description_search" msgid="6749826639098512120">"Qidirish"</string>
<string name="searchview_description_query" msgid="5911778593125355124">"Qidiruv so‘rovi"</string>
<string name="searchview_description_clear" msgid="1330281990951833033">"So‘rovni tozalash"</string>
<string name="searchview_description_submit" msgid="2688450133297983542">"So‘rov yaratish"</string>
@@ -1082,7 +1082,7 @@
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Masshtabni o‘zgartirish uchun ikki marta bosing"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidjet qo‘shilmadi."</string>
<string name="ime_action_go" msgid="8320845651737369027">"O‘tish"</string>
- <string name="ime_action_search" msgid="658110271822807811">"Izlash"</string>
+ <string name="ime_action_search" msgid="658110271822807811">"Qidirish"</string>
<string name="ime_action_send" msgid="2316166556349314424">"Jo‘natish"</string>
<string name="ime_action_next" msgid="3138843904009813834">"Keyingi"</string>
<string name="ime_action_done" msgid="8971516117910934605">"Tayyor"</string>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 9044802..919519e 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -51,4 +51,10 @@
<!-- Scale factor threshold used by the screen magnifier to determine when to switch from
panning to scaling the magnification viewport. -->
<item name="config_screen_magnification_scaling_threshold" format="float" type="dimen">0.1</item>
+
+ <!-- Do not show the message saying USB is connected in charging mode. -->
+ <bool name="config_usbChargingMessage">false</bool>
+
+ <!-- Use a custom transition for RemoteViews. -->
+ <bool name="config_overrideRemoteViewsActivityTransition">true</bool>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 48bfe28..a6eb68b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -162,6 +162,9 @@
<!-- Text color, typeface, size, and style for small text inside of a popup menu. -->
<attr name="textAppearanceSmallPopupMenu" format="reference" />
+ <!-- Text color, typeface, size, and style for header text inside of a popup menu. -->
+ <attr name="textAppearancePopupMenuHeader" format="reference" />
+
<!-- The underline color and thickness for easy correct suggestion -->
<attr name="textAppearanceEasyCorrectSuggestion" format="reference" />
@@ -729,6 +732,8 @@
<attr name="listPopupWindowStyle" format="reference" />
<!-- Default PopupMenu style. -->
<attr name="popupMenuStyle" format="reference" />
+ <!-- Default context menu PopupMenu style. -->
+ <attr name="contextPopupMenuStyle" format="reference" />
<!-- Default StackView style. -->
<attr name="stackViewStyle" format="reference" />
@@ -2150,6 +2155,13 @@
(which is exiting the screen). The wallpaper remains
static behind the animation. -->
<attr name="wallpaperIntraCloseExitAnimation" format="reference" />
+
+ <!-- When opening a new activity from a RemoteViews, this is the
+ animation that is run on the next activity (which is entering the
+ screen). Requires config_overrideRemoteViewsActivityTransition to
+ be true. -->
+ <attr name="activityOpenRemoteViewsEnterAnimation" format="reference" />
+
</declare-styleable>
<!-- ============================= -->
@@ -5550,6 +5562,8 @@
<!-- Push object to the end of its container, not changing its size. -->
<flag name="end" value="0x00800005" />
</attr>
+ <!-- Specifies the initial drawable level in the range 0 to 10000. -->
+ <attr name="level" format="integer" />
<!-- Reference to a drawable resource to draw with the specified scale. -->
<attr name="drawable" />
<!-- Use the drawable's intrinsic width and height as minimum size values.
@@ -7769,10 +7783,24 @@
<attr name="title" />
<attr name="subtitle" />
<attr name="gravity" />
- <attr name="titleMargins" format="dimension" />
+ <!-- Specifies extra space on the left, start, right and end sides
+ of the toolbar's title. Margin values should be positive. -->
+ <attr name="titleMargin" format="dimension" />
+ <!-- Specifies extra space on the start side of the toolbar's title.
+ If both this attribute and titleMargin are specified, then this
+ attribute takes precedence. Margin values should be positive. -->
<attr name="titleMarginStart" format="dimension" />
+ <!-- Specifies extra space on the end side of the toolbar's title.
+ If both this attribute and titleMargin are specified, then this
+ attribute takes precedence. Margin values should be positive. -->
<attr name="titleMarginEnd" format="dimension" />
+ <!-- Specifies extra space on the top side of the toolbar's title.
+ If both this attribute and titleMargin are specified, then this
+ attribute takes precedence. Margin values should be positive. -->
<attr name="titleMarginTop" format="dimension" />
+ <!-- Specifies extra space on the bottom side of the toolbar's title.
+ If both this attribute and titleMargin are specified, then this
+ attribute takes precedence. Margin values should be positive. -->
<attr name="titleMarginBottom" format="dimension" />
<attr name="contentInsetStart" />
<attr name="contentInsetEnd" />
diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml
index c29fec6..9d1dda5 100644
--- a/core/res/res/values/colors_holo.xml
+++ b/core/res/res/values/colors_holo.xml
@@ -79,8 +79,7 @@
<eat-comment />
<color name="holo_primary_dark">#ff000000</color>
- <color name="holo_primary">#ffe6e6e6</color>
- <color name="holo_primary_light">#ffffffff</color>
+ <color name="holo_primary">#ff222222</color>
<color name="holo_control_activated">@color/holo_blue_light</color>
<color name="holo_control_normal">#39cccccc</color>
<color name="holo_button_pressed">#59f0f0f0</color>
@@ -88,8 +87,7 @@
<color name="holo_light_primary_dark">#ff000000</color>
<color name="holo_light_primary">#ffe6e6e6</color>
- <color name="holo_light_primary_light">#ffffffff</color>
- <color name="holo_light_control_activated">@color/holo_control_activated</color>
+ <color name="holo_light_control_activated">@color/holo_blue_light</color>
<color name="holo_light_control_normal">#dacccccc</color>
<color name="holo_light_button_pressed">#66666666</color>
<color name="holo_light_button_normal">#b3cccccc</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
old mode 100755
new mode 100644
index 3599b5b..24d760f
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -252,7 +252,7 @@
<integer name="config_networkTransitionTimeout">60000</integer>
<!-- List of regexpressions describing the interface (if any) that represent tetherable
- USB interfaces. If the device doesn't want to support tething over USB this should
+ USB interfaces. If the device doesn't want to support tethering over USB this should
be empty. An example would be "usb.*" -->
<string-array translatable="false" name="config_tether_usb_regexs">
</string-array>
@@ -410,6 +410,9 @@
radio is unable to find any MCC information to infer wifi country code from -->
<bool translatable="false" name="config_wifi_revert_country_code_on_cellular_loss">false</bool>
+ <!-- Boolean indicating whether or not wifi firmware debugging is enabled -->
+ <bool translatable="false" name="config_wifi_enable_wifi_firmware_debugging">false</bool>
+
<!-- Integer specifying the basic autojoin parameters -->
<integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_threshold">-65</integer>
<integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_factor">5</integer>
@@ -543,6 +546,9 @@
<!-- If this is true, the screen will come on when you unplug usb/power/whatever. -->
<bool name="config_unplugTurnsOnScreen">false</bool>
+ <!-- If this is true, the message that USB is only being used for charging will be shown. -->
+ <bool name="config_usbChargingMessage">true</bool>
+
<!-- Set this true only if the device has separate attention and notification lights. -->
<bool name="config_useAttentionLight">false</bool>
@@ -1756,6 +1762,28 @@
-->
<bool name="config_enableWifiDisplay">false</bool>
+ <!-- The color transform values that correspond to each respective configuration mode for the
+ built-in display, or -1 if the mode is unsupported by the device. The possible
+ configuration modes are:
+ 1. Wide-gamut ("Vibrant")
+ 2. Adobe RGB ("Natural")
+ 3. sRGB ("Standard")
+
+ For example, if a device had Wide-gamut as color transform mode 1, sRGB mode as color
+ transform mode 7, and did not support Adobe RGB at all this would look like:
+
+ <integer-array name="config_colorTransforms">
+ <item>1</item>
+ <item>-1</item>
+ <item>7</item>
+ </integer-array>
+ -->
+ <integer-array name="config_colorTransforms">
+ <item>-1</item>
+ <item>-1</item>
+ <item>-1</item>
+ </integer-array>
+
<!-- When true use the linux /dev/input/event subsystem to detect the switch changes
on the headphone/microphone jack. When false use the older uevent framework. -->
<bool name="config_useDevInputEventForAudioJack">false</bool>
@@ -2186,6 +2214,10 @@
<bool name="config_defaultWindowFeatureOptionsPanel">true</bool>
<bool name="config_defaultWindowFeatureContextMenu">true</bool>
+ <!-- If true, the transition for a RemoteViews is read from a resource instead of using the
+ default scale-up transition. -->
+ <bool name="config_overrideRemoteViewsActivityTransition">false</bool>
+
<!-- This config is used to check if the carrier requires converting destination
number before sending out a SMS.
Formats for this configuration as below:
@@ -2297,6 +2329,9 @@
<string-array name="config_cell_retries_per_error_code">
</string-array>
+ <!-- Set initial MaxRetry value for operators -->
+ <integer name="config_mdc_initial_max_retry">1</integer>
+
<!-- The OEM specified sensor type for the gesture to launch the camear app. -->
<integer name="config_cameraLaunchGestureSensorType">-1</integer>
<!-- The OEM specified sensor string type for the gesture to launch camera app, this value
@@ -2306,4 +2341,12 @@
<!-- Whether to open UI submenus side by side with the top menu (as opposed to
replacing the top menu). -->
<bool name="config_enableCascadingSubmenus">false</bool>
+
+ <!-- Allow the gesture to double tap the power button twice to start the camera while the device
+ is non-interactive. -->
+ <bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
+
+ <!-- Name of the component to handle network policy notifications. If present,
+ disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
+ <string translatable="false" name="config_networkPolicyNotificationComponent"></string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 09c1e6f..2621bc9 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -37,7 +37,7 @@
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
<dimen name="navigation_bar_height_landscape">48dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">42dp</dimen>
+ <dimen name="navigation_bar_width">48dp</dimen>
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">24dip</dimen>
<!-- Size of the giant number (unread count) in the notifications -->
@@ -45,6 +45,9 @@
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
<dimen name="status_bar_edge_ignore">5dp</dimen>
+ <!-- Width of a divider bar used to resize docked stacks. -->
+ <dimen name="docked_stack_divider_thickness">24dp</dimen>
+
<!-- Min width for a tablet device -->
<dimen name="min_xlarge_screen_width">800dp</dimen>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 55bea9e..96a81d1 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -72,6 +72,7 @@
<dimen name="text_size_title_material_toolbar">20dp</dimen>
<dimen name="text_size_subtitle_material_toolbar">16dp</dimen>
<dimen name="text_size_menu_material">16sp</dimen>
+ <dimen name="text_size_menu_header_material">14sp</dimen>
<dimen name="text_size_body_2_material">14sp</dimen>
<dimen name="text_size_body_1_material">14sp</dimen>
<dimen name="text_size_caption_material">12sp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a2b6a7b..f4d0b39 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2662,6 +2662,20 @@
<public type="attr" name="listMenuViewStyle" />
<public type="attr" name="subMenuArrow" />
+ <public type="attr" name="activityWidth" />
+ <public type="attr" name="activityHeight" />
+ <public type="attr" name="resizeableActivity" />
+ <public type="attr" name="titleMargin" />
+ <public type="attr" name="titleMarginStart" />
+ <public type="attr" name="titleMarginEnd" />
+ <public type="attr" name="titleMarginTop" />
+ <public type="attr" name="titleMarginBottom" />
+ <public type="attr" name="maxButtonHeight" />
+ <public type="attr" name="buttonGravity" />
+ <public type="attr" name="collapseIcon" />
+ <public type="attr" name="level" />
+ <public type="attr" name="contextPopupMenuStyle" />
+ <public type="attr" name="textAppearancePopupMenuHeader" />
<public type="style" name="Theme.Material.DayNight" />
<public type="style" name="Theme.Material.DayNight.DarkActionBar" />
@@ -2682,8 +2696,5 @@
<public type="style" name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" />
<public type="id" name="accessibilityActionSetProgress" />
- <public type="attr" name="activityWidth" />
- <public type="attr" name="activityHeight" />
- <public type="attr" name="resizeableActivity" />
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 4bad16d..11c4cc0 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1232,7 +1232,7 @@
<item name="titleTextAppearance">@style/TextAppearance.Widget.Toolbar.Title</item>
<item name="subtitleTextAppearance">@style/TextAppearance.Widget.Toolbar.Subtitle</item>
<item name="minHeight">?attr/actionBarSize</item>
- <item name="titleMargins">4dp</item>
+ <item name="titleMargin">4dp</item>
<item name="maxButtonHeight">56dp</item>
<item name="buttonGravity">top</item>
<item name="navigationButtonStyle">@style/Widget.Toolbar.Button.Navigation</item>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 5dc14e3..38a1693 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -307,6 +307,10 @@
<style name="TextAppearance.Material.Widget.PopupMenu"/>
<style name="TextAppearance.Material.Widget.PopupMenu.Large" parent="TextAppearance.Material.Menu" />
<style name="TextAppearance.Material.Widget.PopupMenu.Small" parent="TextAppearance.Material.Menu" />
+ <style name="TextAppearance.Material.Widget.PopupMenu.Header" parent="TextAppearance.Material.Subhead">
+ <item name="fontFamily">@string/font_family_title_material</item>
+ <item name="textSize">@dimen/text_size_menu_header_material</item>
+ </style>
<style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" />
@@ -471,6 +475,7 @@
<style name="Widget.Material.Button.Colored">
<item name="background">@drawable/btn_colored_material</item>
<item name="textAppearance">@style/TextAppearance.Material.Widget.Button.Inverse</item>
+ <item name="textColor">@color/btn_colored_text_material</item>
</style>
<!-- Small bordered ink button -->
@@ -487,7 +492,7 @@
<!-- Colored borderless ink button -->
<style name="Widget.Material.Button.Borderless.Colored">
- <item name="textColor">@color/btn_colored_text_material</item>
+ <item name="textColor">@color/btn_colored_borderless_text_material</item>
</style>
<!-- Alert dialog button bar button -->
@@ -862,6 +867,11 @@
<item name="dropDownHorizontalOffset">-4dip</item>
</style>
+ <style name="Widget.Material.ContextPopupMenu" parent="Widget.Material.ListPopupWindow">
+ <item name="overlapAnchor">true</item>
+ <item name="popupEnterTransition">@null</item>
+ </style>
+
<style name="Widget.Material.ActionButton">
<item name="background">?attr/actionBarItemBackground</item>
<item name="paddingStart">12dp</item>
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index c6052ff..05835e7 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -18,6 +18,7 @@
<style name="Animation.Micro.Activity" parent="Animation.Material.Activity">
<item name="activityOpenEnterAnimation">@anim/slide_in_micro</item>
+ <item name="activityOpenRemoteViewsEnterAnimation">@anim/slide_in_micro</item>
<item name="activityOpenExitAnimation">@null</item>
<item name="activityCloseEnterAnimation">@null</item>
<item name="activityCloseExitAnimation">@anim/slide_out_micro</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
old mode 100755
new mode 100644
index ed4391b..06de81d
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -307,6 +307,7 @@
<java-symbol type="bool" name="config_wifi_enable_disconnection_debounce" />
<java-symbol type="bool" name="config_wifi_enable_5GHz_preference" />
<java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
+ <java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
<java-symbol type="bool" name="config_supportMicNearUltrasound" />
<java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
@@ -408,6 +409,7 @@
<java-symbol type="integer" name="config_volte_replacement_rat"/>
<java-symbol type="integer" name="config_valid_wappush_index" />
<java-symbol type="integer" name="config_overrideHasPermanentMenuKey" />
+ <java-symbol type="integer" name="config_mdc_initial_max_retry" />
<java-symbol type="bool" name="config_hasPermanentDpad" />
<java-symbol type="color" name="tab_indicator_text_v4" />
@@ -1132,6 +1134,7 @@
<java-symbol type="array" name="config_telephonyHardware" />
<java-symbol type="array" name="config_keySystemUuidMapping" />
<java-symbol type="array" name="config_gpsParameters" />
+ <java-symbol type="array" name="config_colorTransforms" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="indicator_input_error" />
@@ -1287,6 +1290,7 @@
<java-symbol type="layout" name="number_picker" />
<java-symbol type="layout" name="permissions_package_list_item" />
<java-symbol type="layout" name="popup_menu_item_layout" />
+ <java-symbol type="layout" name="popup_menu_header_item_layout" />
<java-symbol type="layout" name="remote_views_adapter_default_loading_view" />
<java-symbol type="layout" name="search_bar" />
<java-symbol type="layout" name="search_dropdown_item_icons_2line" />
@@ -1475,6 +1479,7 @@
<java-symbol type="bool" name="config_showNavigationBar" />
<java-symbol type="bool" name="config_supportAutoRotation" />
<java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
+ <java-symbol type="dimen" name="docked_stack_divider_thickness" />
<java-symbol type="dimen" name="navigation_bar_height" />
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />
@@ -1632,6 +1637,7 @@
<java-symbol type="bool" name="config_enableNetworkLocationOverlay" />
<java-symbol type="bool" name="config_sf_limitedAlpha" />
<java-symbol type="bool" name="config_unplugTurnsOnScreen" />
+ <java-symbol type="bool" name="config_usbChargingMessage" />
<java-symbol type="bool" name="config_allowAutoBrightnessWhileDozing" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromUnplug" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromGesture" />
@@ -1716,6 +1722,7 @@
<java-symbol type="integer" name="config_undockedHdmiRotation" />
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
+ <java-symbol type="layout" name="docked_stack_divider" />
<java-symbol type="layout" name="launch_warning" />
<java-symbol type="layout" name="safe_mode" />
<java-symbol type="layout" name="simple_list_item_2_single_choice" />
@@ -2185,6 +2192,7 @@
<java-symbol type="bool" name="config_sms_force_7bit_encoding" />
<java-symbol type="bool" name="config_defaultWindowFeatureOptionsPanel" />
<java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
+ <java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" />
<java-symbol type="layout" name="simple_account_item" />
<java-symbol type="array" name="config_sms_convert_destination_number_support" />
@@ -2315,6 +2323,7 @@
<!-- Gesture -->
<java-symbol type="integer" name="config_cameraLaunchGestureSensorType" />
<java-symbol type="string" name="config_cameraLaunchGestureSensorStringType" />
+ <java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
<java-symbol type="drawable" name="platlogo_m" />
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 3010190..59dfc92 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -93,6 +93,7 @@
<item name="textAppearanceLargePopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Large</item>
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Small</item>
+ <item name="textAppearancePopupMenuHeader">@style/TextAppearance.Material.Widget.PopupMenu.Header</item>
<!-- Button styles -->
<item name="buttonStyle">@style/Widget.Material.Button</item>
@@ -283,6 +284,7 @@
<item name="stackViewStyle">@style/Widget.Material.StackView</item>
<item name="activityChooserViewStyle">@style/Widget.Material.ActivityChooserView</item>
<item name="fragmentBreadCrumbsStyle">@style/Widget.Material.FragmentBreadCrumbs</item>
+ <item name="contextPopupMenuStyle">@style/Widget.Material.ContextPopupMenu</item>
<!-- Preference styles -->
<item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
@@ -449,6 +451,7 @@
<item name="textAppearanceLargePopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Large</item>
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Small</item>
+ <item name="textAppearancePopupMenuHeader">@style/TextAppearance.Material.Widget.PopupMenu.Header</item>
<!-- Button styles -->
<item name="buttonStyle">@style/Widget.Material.Light.Button</item>
@@ -640,6 +643,7 @@
<item name="stackViewStyle">@style/Widget.Material.Light.StackView</item>
<item name="activityChooserViewStyle">@style/Widget.Material.Light.ActivityChooserView</item>
<item name="fragmentBreadCrumbsStyle">@style/Widget.Material.FragmentBreadCrumbs</item>
+ <item name="contextPopupMenuStyle">@style/Widget.Material.ContextPopupMenu</item>
<!-- Preference styles -->
<item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 28d99d8..ddd0ca2 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -47,17 +47,38 @@
<value>0.2</value> <!-- ~2mA -->
<value>0.1</value> <!-- ~1mA -->
</array>
- <!-- Different CPU speeds as reported in
- /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state -->
- <array name="cpu.speeds">
+
+ <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
+ number of CPU cores for that cluster.
+
+ Ex:
+ <array name="cpu.clusters.cores">
+ <value>4</value> // cluster 0 has cpu0, cpu1, cpu2, cpu3
+ <value>2</value> // cluster 1 has cpu4, cpu5
+ </array> -->
+ <array name="cpu.clusters.cores">
+ <value>1</value> <!-- cluster 0 has cpu0 -->
+ </array>
+
+ <!-- Different CPU speeds for cluster 0 as reported in
+ /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state.
+
+ There must be one of these for each cluster, labeled:
+ cpu.speeds.cluster0, cpu.speeds.cluster1, etc... -->
+ <array name="cpu.speeds.cluster0">
<value>400000</value> <!-- 400 MHz CPU speed -->
</array>
- <!-- Current when CPU is idle -->
- <item name="cpu.idle">0.1</item>
- <!-- Current at each CPU speed, as per 'cpu.speeds' -->
- <array name="cpu.active">
+
+ <!-- Current at each CPU speed for cluster 0, as per 'cpu.speeds.cluster0'.
+ Like cpu.speeds.cluster0, there must be one of these present for
+ each heterogeneous CPU cluster. -->
+ <array name="cpu.active.cluster0">
<value>0.1</value> <!-- ~100mA -->
</array>
+
+ <!-- Current when CPU is idle -->
+ <item name="cpu.idle">0.1</item>
+
<!-- This is the battery capacity in mAh (measured at nominal voltage) -->
<item name="battery.capacity">1000</item>
diff --git a/core/tests/BTtraffic/Android.mk b/core/tests/BTtraffic/Android.mk
new file mode 100644
index 0000000..7d83527
--- /dev/null
+++ b/core/tests/BTtraffic/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := bttraffic
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/BTtraffic/AndroidManifest.xml b/core/tests/BTtraffic/AndroidManifest.xml
new file mode 100644
index 0000000..00d9707
--- /dev/null
+++ b/core/tests/BTtraffic/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.experimental.bttraffic" >
+
+ <uses-permission android:name="android.permission.BLUETOOTH"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+
+ <uses-sdk
+ android:minSdkVersion="18"
+ android:targetSdkVersion="18"
+ />
+ <application
+ android:allowBackup="false"
+ android:label="@string/app_name" >
+ <service
+ android:name=".BTtraffic"
+ android:enabled="true"
+ android:exported="true" >
+ </service>
+ </application>
+
+</manifest>
diff --git a/core/tests/BTtraffic/README b/core/tests/BTtraffic/README
new file mode 100644
index 0000000..430488f
--- /dev/null
+++ b/core/tests/BTtraffic/README
@@ -0,0 +1,45 @@
+This is a tool to generate classic Bluetooth traffic with specified period and package size.
+Together with the SvcMonitor, which will be called automatically in this android service, can be
+used to measure the CPU usage from the Java layer Bluetooth code and the underlying system service
+com.android.bluetooth.
+
+1. Server (Listener) - Client (Sender) model. Both run as an Android service.
+2. No pairing needed. Communicate via unsecured RFcomm. Client establishes the connection by
+providing the MAC addr of the server.
+3. Bluetooth has to be turned on on both side.
+4. Client can configure the traffic by specifying the transfer period and package size.
+5. A separate monitor process will be automatically forked and will be reading from /proc file
+system to calculate the cpu usage. The measurement is updated once per second.
+6. The monitor process (com.google.android.experimental.svcmonitor/.ScvMonitor) can be run as an
+independent service to measure cpu usage on any similarly configured tests (e.g. wifi, BLE). Refer
+to SvcMonitor's README for usage and details.
+
+Usage:
+To instal the test:
+On both the server and client device, install the 2 apk:
+$ adb install $OUT/system/app/bttraffic/bttraffic.apk
+$ adb install $OUT/system/app/svcmonitor/svcmonitor.apk
+
+To start the service on the SERVER side:
+$ adb shell am startservice -a start --ez ack true \
+com.google.android.experimental.bttraffic/.BTtraffic
+
+To start the service on the CLIENT side:
+$ adb shell am startservice -a start \
+-e addr "F8:A9:D0:A8:74:8E" --ei size 1000 --ei period 15 \
+com.google.android.experimental.bttraffic/.BTtraffic
+
+To stop the test:
+On either the server or client:
+$ adb shell am startservice -a stop \
+com.google.android.experimental.bttraffic/.BTtraffic
+
+To look at the data:
+$ adb logcat | grep bttraffic
+
+Options:
+-e addr: MAC addr of the server, in uppercase letter.
+--ei size: package size, unit: byte; default: 1024, MAX: 20MB
+--ei period: system sleep time between sending each package, unit: ms, default: 5000
+ ** if -1 is provided, client will only send the package once.
+--ez ack: whether acknowledge is required (true/false)
diff --git a/core/tests/BTtraffic/res/values/strings.xml b/core/tests/BTtraffic/res/values/strings.xml
new file mode 100644
index 0000000..e70276e
--- /dev/null
+++ b/core/tests/BTtraffic/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Bluetooth Test</string>
+</resources>
diff --git a/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java
new file mode 100644
index 0000000..286c0aa
--- /dev/null
+++ b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java
@@ -0,0 +1,328 @@
+package com.google.android.experimental.bttraffic;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.Exception;
+import java.lang.Runtime;
+import java.lang.RuntimeException;
+import java.lang.Process;
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+
+public class BTtraffic extends Service {
+ public static final String TAG = "bttraffic";
+ static final String SERVICE_NAME = "bttraffic";
+ static final String SYS_SERVICE_NAME = "com.android.bluetooth";
+ static final UUID SERVICE_UUID = UUID.fromString("5e8945b0-1234-5432-a5e2-0800200c9a67");
+ volatile Thread mWorkerThread;
+ volatile boolean isShuttingDown = false;
+ volatile boolean isServer = false;
+
+ public BTtraffic() {}
+
+ static void safeClose(Closeable closeable) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ Log.d(TAG, "Unable to close resource.\n");
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent == null) {
+ stopSelf();
+ return 0;
+ }
+ if ("stop".equals(intent.getAction())) {
+ stopService();
+ } else if ("start".equals(intent.getAction())) {
+ startWorker(intent);
+ } else {
+ Log.d(TAG, "unknown action: + " + intent.getAction());
+ }
+ return 0;
+ }
+
+ private void startWorker(Intent intent) {
+ if (mWorkerThread != null) {
+ Log.d(TAG, "worker thread already active");
+ return;
+ }
+ isShuttingDown = false;
+ String remoteAddr = intent.getStringExtra("addr");
+ Log.d(TAG, "startWorker: addr=" + remoteAddr);
+ Runnable worker =
+ remoteAddr == null
+ ? new ListenerRunnable(this, intent)
+ : new SenderRunnable(this, remoteAddr, intent);
+ isServer = remoteAddr == null ? true: false;
+ mWorkerThread = new Thread(worker, "BTtrafficWorker");
+ try {
+ startMonitor();
+ Log.d(TAG, "Monitor service started");
+ mWorkerThread.start();
+ Log.d(TAG, "Worker thread started");
+ } catch (Exception e) {
+ Log.d(TAG, "Failed to start service", e);
+ }
+ }
+
+ private void startMonitor()
+ throws Exception {
+ if (isServer) {
+ Log.d(TAG, "Start monitor on server");
+ String[] startmonitorCmd = {
+ "/system/bin/am",
+ "startservice",
+ "-a", "start",
+ "-e", "java", SERVICE_NAME,
+ "-e", "hal", SYS_SERVICE_NAME,
+ "com.google.android.experimental.svcmonitor/.SvcMonitor"
+ };
+ Process ps = new ProcessBuilder()
+ .command(startmonitorCmd)
+ .redirectErrorStream(true)
+ .start();
+ } else {
+ Log.d(TAG, "No need to start SvcMonitor on client");
+ }
+ }
+
+ private void stopMonitor()
+ throws Exception {
+ if (isServer) {
+ Log.d(TAG, "StopMonitor on server");
+ String[] stopmonitorCmd = {
+ "/system/bin/am",
+ "startservice",
+ "-a", "stop",
+ "com.google.android.experimental.svcmonitor/.SvcMonitor"
+ };
+ Process ps = new ProcessBuilder()
+ .command(stopmonitorCmd)
+ .redirectErrorStream(true)
+ .start();
+ } else {
+ Log.d(TAG, "No need to stop Svcmonitor on client");
+ }
+ }
+
+ public void stopService() {
+ if (mWorkerThread == null) {
+ Log.d(TAG, "no active thread");
+ return;
+ }
+
+ isShuttingDown = true;
+
+ try {
+ stopMonitor();
+ } catch (Exception e) {
+ Log.d(TAG, "Unable to stop SvcMonitor!", e);
+ }
+
+ if (Thread.currentThread() != mWorkerThread) {
+ mWorkerThread.interrupt();
+ Log.d(TAG, "Interrupting thread");
+ try {
+ mWorkerThread.join();
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Unable to join thread!");
+ }
+ }
+
+ mWorkerThread = null;
+ stopSelf();
+ Log.d(TAG, "Service stopped");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ public static class ListenerRunnable implements Runnable {
+ private final BTtraffic bttraffic;
+ private final boolean sendAck;
+ private Intent intent;
+ private final int maxbuffersize = 20 * 1024 * 1024;
+
+ public ListenerRunnable(BTtraffic bttraffic, Intent intent) {
+ this.bttraffic = bttraffic;
+ this.sendAck = intent.getBooleanExtra("ack", true);
+ this.intent = intent;
+ }
+
+ @Override
+ public void run() {
+ BluetoothServerSocket serverSocket;
+
+ try {
+ Log.d(TAG, "getting server socket");
+ serverSocket = BluetoothAdapter.getDefaultAdapter()
+ .listenUsingInsecureRfcommWithServiceRecord(
+ SERVICE_NAME, SERVICE_UUID);
+ } catch (IOException e) {
+ Log.d(TAG, "error creating server socket, stopping thread");
+ bttraffic.stopService();
+ return;
+ }
+
+ Log.d(TAG, "got server socket, starting accept loop");
+ BluetoothSocket socket = null;
+ try {
+ Log.d(TAG, "accepting");
+ socket = serverSocket.accept();
+
+ if (!Thread.interrupted()) {
+ Log.d(TAG, "accepted, listening");
+ doListening(socket.getInputStream(), socket.getOutputStream());
+ Log.d(TAG, "listen finished");
+ }
+ } catch (IOException e) {
+ Log.d(TAG, "error while accepting or listening", e);
+ } finally {
+ Log.d(TAG, "Linster interruped");
+ Log.d(TAG, "closing socket and stopping service");
+ safeClose(serverSocket);
+ safeClose(socket);
+ if (!bttraffic.isShuttingDown)
+ bttraffic.stopService();
+ }
+
+ }
+
+ private void doListening(InputStream inputStream, OutputStream outputStream)
+ throws IOException {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(maxbuffersize);
+
+ while (!Thread.interrupted()) {
+ readBytesIntoBuffer(inputStream, byteBuffer, 4);
+ byteBuffer.flip();
+ int length = byteBuffer.getInt();
+ if (Thread.interrupted())
+ break;
+ readBytesIntoBuffer(inputStream, byteBuffer, length);
+
+ if (sendAck)
+ outputStream.write(0x55);
+ }
+ }
+
+ void readBytesIntoBuffer(InputStream inputStream, ByteBuffer byteBuffer, int numToRead)
+ throws IOException {
+ byteBuffer.clear();
+ while (true) {
+ int position = byteBuffer.position();
+ int remaining = numToRead - position;
+ if (remaining == 0) {
+ break;
+ }
+ int count = inputStream.read(byteBuffer.array(), position, remaining);
+ if (count < 0) {
+ throw new IOException("read the EOF");
+ }
+ byteBuffer.position(position + count);
+ }
+ }
+ }
+
+ public static class SenderRunnable implements Runnable {
+ private final BTtraffic bttraffic;
+ private final String remoteAddr;
+ private final int pkgsize, period;
+ private final int defaultpkgsize = 1024;
+ private final int defaultperiod = 5000;
+ private static ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
+
+ public SenderRunnable(BTtraffic bttraffic, String remoteAddr, Intent intent) {
+ this.bttraffic = bttraffic;
+ this.remoteAddr = remoteAddr;
+ this.pkgsize = intent.getIntExtra("size", defaultpkgsize);
+ this.period = intent.getIntExtra("period", defaultperiod);
+ }
+
+ @Override
+ public void run() {
+ BluetoothDevice device = null;
+ try {
+ device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "Invalid BT MAC address!\n");
+ }
+ if (device == null) {
+ Log.d(TAG, "can't find matching device, stopping thread and service");
+ bttraffic.stopService();
+ return;
+ }
+
+ BluetoothSocket socket = null;
+ try {
+ Log.d(TAG, "connecting to device with MAC addr: " + remoteAddr);
+ socket = device.createInsecureRfcommSocketToServiceRecord(SERVICE_UUID);
+ socket.connect();
+ Log.d(TAG, "connected, starting to send");
+ doSending(socket.getOutputStream());
+ Log.d(TAG, "send stopped, stopping service");
+ } catch (Exception e) {
+ Log.d(TAG, "error while sending", e);
+ } finally {
+ Log.d(TAG, "finishing, closing thread and service");
+ safeClose(socket);
+ if (!bttraffic.isShuttingDown)
+ bttraffic.stopService();
+ }
+ }
+
+ private void doSending(OutputStream outputStream) throws IOException {
+ Log.w(TAG, "doSending");
+ try {
+ Random random = new Random(System.currentTimeMillis());
+
+ byte[] bytes = new byte[pkgsize];
+ random.nextBytes(bytes);
+ while (!Thread.interrupted()) {
+ writeBytes(outputStream, bytes.length);
+ outputStream.write(bytes, 0, bytes.length);
+ if (period < 0)
+ break;
+ if (period == 0)
+ continue;
+
+ SystemClock.sleep(period);
+ }
+ Log.d(TAG, "Sender interrupted");
+ } catch (IOException e) {
+ Log.d(TAG, "doSending got error", e);
+ }
+ }
+
+ private static void writeBytes(OutputStream outputStream, int value) throws IOException {
+ lengthBuffer.putInt(value);
+ lengthBuffer.flip();
+ outputStream.write(lengthBuffer.array(), lengthBuffer.position(), lengthBuffer.limit());
+ }
+ }
+
+}
diff --git a/core/tests/SvcMonitor/Android.mk b/core/tests/SvcMonitor/Android.mk
new file mode 100644
index 0000000..2b80455
--- /dev/null
+++ b/core/tests/SvcMonitor/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := svcmonitor
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/SvcMonitor/AndroidManifest.xml b/core/tests/SvcMonitor/AndroidManifest.xml
new file mode 100644
index 0000000..de5a9bd
--- /dev/null
+++ b/core/tests/SvcMonitor/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.experimental.svcmonitor" >
+
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+
+ <uses-sdk
+ android:minSdkVersion="18"
+ android:targetSdkVersion="18"
+ />
+ <application
+ android:allowBackup="false"
+ android:label="@string/app_name" >
+ <service
+ android:name=".SvcMonitor"
+ android:enabled="true"
+ android:exported="true" >
+ </service>
+ </application>
+
+</manifest>
diff --git a/core/tests/SvcMonitor/README b/core/tests/SvcMonitor/README
new file mode 100644
index 0000000..13a4380
--- /dev/null
+++ b/core/tests/SvcMonitor/README
@@ -0,0 +1,27 @@
+This Android service measures CPU usage of a program and an underlying system service it relies on.
+An example of this would be an android app XYZ communicates to some other device via Bluetooth. The
+SvcMonitor service can monitor the CPU usage of XYZ and com.android.bluetooth.
+
+Usage:
+
+To start the service:
+$ adb shell am startservice -a start \
+-e java XYZ -e hal com.android.bluetooth \
+com.google.android.experimental.svcmonitor/.SvcMonitor
+
+To stop the service:
+$ adb shell am startservice -a stop \
+com.google.android.experimental.svcmonitor/.SvcMonitor
+
+To stop the service config:
+$ adb shell am startservice -a change \
+-e java NewName -e hal NewService \
+com.google.android.experimental.svcmonitor/.SvcMonitor
+
+To monitor the data:
+$ adb logcat | grep XYZ
+
+Options:
+-e java NameOfProgram: any running process’s name.
+-e hal NameOfSysService: name of the system service the previous process relies on.
+--ei period: period between each measurement (frequency). Unit: ms, Default:1000, Min: 100
diff --git a/core/tests/SvcMonitor/res/values/strings.xml b/core/tests/SvcMonitor/res/values/strings.xml
new file mode 100644
index 0000000..e70276e
--- /dev/null
+++ b/core/tests/SvcMonitor/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Bluetooth Test</string>
+</resources>
diff --git a/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java
new file mode 100644
index 0000000..a451445
--- /dev/null
+++ b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java
@@ -0,0 +1,209 @@
+package com.google.android.experimental.svcmonitor;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.lang.Runnable;
+import java.lang.Thread;
+import java.util.Set;
+
+public class SvcMonitor extends Service {
+ public static final String TAG = "svcmonitor";
+ String javaProc, halProc;
+ volatile Thread tMonitor;
+ int period;
+
+ public SvcMonitor() {};
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent == null) {
+ stopSelf();
+ return 0;
+ }
+ Log.d(TAG, "Starting SvcMonitor");
+ if ("stop".equals(intent.getAction())) {
+ stopService();
+ } else if ("start".equals(intent.getAction())) {
+ startMonitor(intent);
+ } else if ("change".equals(intent.getAction())) {
+ changeConfig(intent);
+ } else {
+ Log.d(TAG, "unknown action: + " + intent.getAction());
+ }
+ return 0;
+ }
+
+ private void changeConfig(Intent intent) {
+ if (tMonitor == null) {
+ Log.d(TAG, "Service not active. Start service first");
+ return;
+ }
+ stopThread();
+ startMonitor(intent);
+ }
+
+ private void startMonitor(Intent intent) {
+ if (tMonitor != null) {
+ Log.d(TAG, "thread already active");
+ return;
+ }
+ javaProc = intent.getStringExtra("java");
+ halProc = intent.getStringExtra("hal");
+ period = intent.getIntExtra("period", 1000);
+ if (javaProc == null || halProc == null || period < 100) {
+ Log.d(TAG, "Failed starting monitor, invalid arguments.");
+ stopSelf();
+ return;
+ }
+ Runnable monitor = new MonitorRunnable(this);
+ tMonitor = new Thread(monitor);
+ tMonitor.start();
+ }
+
+ private void stopService() {
+ stopThread();
+ stopSelf();
+ Log.d(TAG, "SvcMonitor stopped");
+ }
+
+ private void stopThread() {
+ if (tMonitor == null) {
+ Log.d(TAG, "no active thread");
+ return;
+ }
+ Log.d(TAG, "interrupting monitor thread");
+ tMonitor.interrupt();
+ try {
+ tMonitor.join();
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Unable to finish monitor thread");
+ }
+ tMonitor = null;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ public static class MonitorRunnable implements Runnable {
+ long java_time_old, hal_time_old, cpu_time_old = -1;
+ String javaPID, halPID;
+ SvcMonitor svcmonitor;
+ static String javaProcTAG;
+ int period;
+
+ public MonitorRunnable(SvcMonitor svcmonitor) {
+ this.svcmonitor = svcmonitor;
+ this.period = svcmonitor.period;
+ javaPID = getPIDof(svcmonitor.javaProc);
+ halPID = getPIDof(svcmonitor.halProc);
+ java_time_old = getPsTime(javaPID);
+ hal_time_old = getPsTime(halPID);
+ cpu_time_old = getPsTime("");
+ javaProcTAG = String.valueOf(svcmonitor.javaProc.toCharArray());
+ }
+
+ @Override
+ public void run() {
+ if (halPID.isEmpty() || javaPID.isEmpty()) {
+ Log.d(javaProcTAG, "No such process: " +
+ (halPID.isEmpty() ? svcmonitor.halProc : svcmonitor.javaProc));
+ return;
+ }
+ while (!Thread.interrupted()) {
+ calculateUsage();
+ SystemClock.sleep(period);
+ }
+ Log.d(TAG, "Stopping monitor thread");
+ }
+
+ private void calculateUsage() {
+ long java_time = getPsTime(javaPID);
+ long hal_time = getPsTime(halPID);
+ long cpu_time = getPsTime("");
+
+ if (cpu_time_old >= 0) {
+ float java_diff = (float) (java_time - java_time_old);
+ float hal_diff = (float) (hal_time - hal_time_old);
+ float cpu_diff = (float) (cpu_time - cpu_time_old);
+ Log.w(javaProcTAG, "\n----------------\n");
+ Log.w(javaProcTAG, "JAVA level CPU: "
+ + (java_diff * 100.0 / cpu_diff) + "%\n");
+ Log.w(javaProcTAG, " HAL level CPU: "
+ + (hal_diff * 100.0 / cpu_diff) + "%\n");
+ Log.w(javaProcTAG, " SYS level CPU: "
+ + ((java_diff + hal_diff) * 100.0 / cpu_diff) + "%\n");
+ } else {
+ Log.w(TAG, "Waiting for status\n");
+ }
+
+ java_time_old = java_time;
+ hal_time_old = hal_time;
+ cpu_time_old = cpu_time;
+ }
+
+ private String getPIDof(String psName) {
+ String pid = "";
+
+ try {
+ String[] cmd = {"/system/bin/sh", "-c", "ps | grep " + psName};
+ Process ps = Runtime.getRuntime().exec(cmd);
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(ps.getInputStream()));
+ String temp = in.readLine();
+ if (temp == null || temp.isEmpty())
+ throw new IOException("No such process: " + psName);
+ pid = temp.split(" +")[1];
+ in.close();
+ } catch (IOException e) {
+ Log.d(javaProcTAG, "Error finding PID of process: " + psName + "\n", e);
+ }
+ return pid;
+ }
+
+ private long getPsTime(String pid) {
+ String psStat = getPsStat("/" + pid);
+ String[] statBreakDown = psStat.split(" +");
+ long psTime;
+
+ if (pid.isEmpty()) {
+ psTime = Long.parseLong(statBreakDown[1])
+ + Long.parseLong(statBreakDown[2])
+ + Long.parseLong(statBreakDown[3])
+ + Long.parseLong(statBreakDown[4]);
+ } else {
+ psTime = Long.parseLong(statBreakDown[13])
+ + Long.parseLong(statBreakDown[14]);
+ }
+
+ return psTime;
+ }
+
+ private String getPsStat(String psname) {
+ String stat = "";
+ try {
+ FileInputStream fs = new FileInputStream("/proc" + psname + "/stat");
+ BufferedReader br = new BufferedReader(new InputStreamReader(fs));
+ stat = br.readLine();
+ fs.close();
+ } catch (IOException e) {
+ Log.d(TAG, "Error retreiving stat. \n");
+ }
+ return stat;
+ }
+ }
+}
diff --git a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
index e0b616c..3e83010 100644
--- a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
+++ b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
@@ -22,7 +22,7 @@
return 1;
}
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{ "checkFunction", "()I", (void*) checkFunction },
};
diff --git a/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttf b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttf
new file mode 100644
index 0000000..1924c35
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx
new file mode 100644
index 0000000..2b8478d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="space"/>
+ <GlyphID id="9" name="H"/>
+ <GlyphID id="16" name="O"/>
+ </GlyphOrder>
+
+ <head>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Sep 9 08:01:17 2015"/>
+ <modified value="Wed Sep 9 08:48:07 2015"/>
+ <xMin value="30"/>
+ <yMin value="-200"/>
+ <xMax value="629"/>
+ <yMax value="800"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <indexToLocFormat value="0"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <advanceWidthMax value="659"/>
+ <minLeftSideBearing value="0"/>
+ <minRightSideBearing value="30"/>
+ <xMaxExtent value="629"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ <numberOfHMetrics value="18"/>
+ </hhea>
+
+ <maxp>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="0x10000"/>
+ <numGlyphs value="54"/>
+ <maxPoints value="73"/>
+ <maxContours value="10"/>
+ <maxCompositePoints value="0"/>
+ <maxCompositeContours value="0"/>
+ <maxZones value="2"/>
+ <maxTwilightPoints value="12"/>
+ <maxStorage value="28"/>
+ <maxFunctionDefs value="119"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="61"/>
+ <maxSizeOfInstructions value="2967"/>
+ <maxComponentElements value="0"/>
+ <maxComponentDepth value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="H" width="627" lsb="50"/>
+ <mtx name="O" width="659" lsb="30"/>
+ <mtx name="space" width="300" lsb="0"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="0" platEncID="3" language="0">
+ <map code="0x20" name="space"/><!-- SPACE -->
+ <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+ <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+ </cmap_format_4>
+ <cmap_format_4 platformID="3" platEncID="1" language="0">
+ <map code="0x20" name="space"/><!-- SPACE -->
+ <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+ <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+ </cmap_format_4>
+ </cmap>
+
+ <fpgm>
+ <assembly>
+ </assembly>
+ </fpgm>
+
+ <prep>
+ <assembly>
+ </assembly>
+ </prep>
+
+ <cvt>
+ <cv index="0" value="0"/>
+ </cvt>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+
+ <!-- The xMin, yMin, xMax and yMax values
+ will be recalculated by the compiler. -->
+
+ <TTGlyph name=".notdef" xMin="93" yMin="-200" xMax="410" yMax="800">
+ <contour>
+ <pt x="410" y="0" on="1"/>
+ <pt x="50" y="0" on="1"/>
+ <pt x="50" y="700" on="1"/>
+ <pt x="410" y="700" on="1"/>
+ </contour>
+ <instructions><assembly>
+ PUSHB[] 1 2 4 3
+ SLOOP[]
+ PUSH[] -400
+ SHPIX[]
+
+ PUSHB[] 0 3 5 3
+ SLOOP[]
+ PUSH[] 400
+ SHPIX[]
+ </assembly></instructions>
+ </TTGlyph>
+
+ <TTGlyph name="H" xMin="50" yMin="0" xMax="577" yMax="700">
+ <contour>
+ <pt x="50" y="700" on="1"/>
+ <pt x="200" y="700" on="1"/>
+ <pt x="200" y="447" on="1"/>
+ <pt x="427" y="447" on="1"/>
+ <pt x="427" y="700" on="1"/>
+ <pt x="577" y="700" on="1"/>
+ <pt x="577" y="0" on="1"/>
+ <pt x="427" y="0" on="1"/>
+ <pt x="427" y="297" on="1"/>
+ <pt x="200" y="297" on="1"/>
+ <pt x="200" y="0" on="1"/>
+ <pt x="50" y="0" on="1"/>
+ </contour>
+ <instructions><assembly>
+ PUSHB[] 0 11 12 3
+ SLOOP[]
+ PUSH[] -200
+ SHPIX[]
+ PUSHB[] 5 6 13 3
+ SLOOP[]
+ PUSH[] 200
+ SHPIX[]
+ </assembly></instructions>
+ </TTGlyph>
+
+ <TTGlyph name="O" xMin="30" yMin="0" xMax="629" yMax="700">
+ <contour>
+ <pt x="248" y="0" on="0"/>
+ <pt x="111" y="94" on="0"/>
+ <pt x="30" y="255" on="0"/>
+ <pt x="30" y="350" on="1"/>
+ <pt x="30" y="445" on="0"/>
+ <pt x="111" y="606" on="0"/>
+ <pt x="248" y="700" on="0"/>
+ <pt x="330" y="700" on="1"/>
+ <pt x="411" y="700" on="0"/>
+ <pt x="548" y="606" on="0"/>
+ <pt x="629" y="445" on="0"/>
+ <pt x="629" y="350" on="1"/>
+ <pt x="629" y="255" on="0"/>
+ <pt x="548" y="94" on="0"/>
+ <pt x="411" y="0" on="0"/>
+ <pt x="330" y="0" on="1"/>
+ </contour>
+ <contour>
+ <pt x="370" y="150" on="0"/>
+ <pt x="439" y="209" on="0"/>
+ <pt x="480" y="302" on="0"/>
+ <pt x="480" y="350" on="1"/>
+ <pt x="480" y="398" on="0"/>
+ <pt x="439" y="491" on="0"/>
+ <pt x="370" y="550" on="0"/>
+ <pt x="330" y="550" on="1"/>
+ <pt x="289" y="550" on="0"/>
+ <pt x="220" y="491" on="0"/>
+ <pt x="179" y="398" on="0"/>
+ <pt x="179" y="350" on="1"/>
+ <pt x="179" y="302" on="0"/>
+ <pt x="220" y="209" on="0"/>
+ <pt x="289" y="150" on="0"/>
+ <pt x="330" y="150" on="1"/>
+ </contour>
+ <instructions><assembly>
+ PUSH[] 32 -200
+ SHPIX[]
+ PUSHB[] 33 200
+ SHPIX[]
+ </assembly></instructions>
+ </TTGlyph>
+
+ <TTGlyph name="space">
+ <contour></contour>
+ <instructions><assembly>
+ PUSH[] 0 -200
+ SHPIX[]
+ PUSHB[] 1 200
+ SHPIX[]
+ </assembly></instructions>
+ </TTGlyph>
+
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Hinted Advance Width Test
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Hinted Advance Width Test
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ HintedAdvanceWidthTest-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Hinted Advance Width Test
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Hinted Advance Width Test
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ HintedAdvanceWidthTest-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="2.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ <psNames>
+ <!-- This file uses unique glyph names based on the information
+ found in the 'post' table. Since these names might not be unique,
+ we have to invent artificial names in case of clashes. In order to
+ be able to retain the original information, we need a name to
+ ps name mapping for those cases where they differ. That's what
+ you see below.
+ -->
+ </psNames>
+ <extraNames>
+ <!-- following are the name that are not taken from the standard Mac glyph order -->
+ </extraNames>
+ </post>
+
+ <gasp>
+ <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+ </gasp>
+
+</ttFont>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 0fee6c3..9498f4c 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -1168,16 +1168,10 @@
}
boolean checkMediaState(String desired) {
- try {
- String mPath = Environment.getExternalStorageDirectory().getPath();
- String actual = getMs().getVolumeState(mPath);
- if (desired.equals(actual)) {
- return true;
- } else {
- return false;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Exception while checking media state", e);
+ String actual = Environment.getExternalStorageState();
+ if (desired.equals(actual)) {
+ return true;
+ } else {
return false;
}
}
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
new file mode 100644
index 0000000..e97bb33
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.graphics.Paint;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * PaintTest tests {@link Paint}.
+ */
+public class PaintTest extends InstrumentationTestCase {
+ private static final String FONT_PATH = "fonts/HintedAdvanceWidthTest-Regular.ttf";
+
+ static void assertEquals(String message, float[] expected, float[] actual) {
+ if (expected.length != actual.length) {
+ fail(message + " expected array length:<" + expected.length + "> but was:<"
+ + actual.length + ">");
+ }
+ for (int i = 0; i < expected.length; ++i) {
+ if (expected[i] != actual[i]) {
+ fail(message + " expected array element[" +i + "]:<" + expected[i] + ">but was:<"
+ + actual[i] + ">");
+ }
+ }
+ }
+
+ static class HintingTestCase {
+ public final String mText;
+ public final float mTextSize;
+ public final float[] mWidthWithoutHinting;
+ public final float[] mWidthWithHinting;
+
+ public HintingTestCase(String text, float textSize, float[] widthWithoutHinting,
+ float[] widthWithHinting) {
+ mText = text;
+ mTextSize = textSize;
+ mWidthWithoutHinting = widthWithoutHinting;
+ mWidthWithHinting = widthWithHinting;
+ }
+ }
+
+ // Following test cases are only valid for HintedAdvanceWidthTest-Regular.ttf in assets/fonts.
+ HintingTestCase[] HINTING_TESTCASES = {
+ new HintingTestCase("H", 11f, new float[] { 7f }, new float[] { 13f }),
+ new HintingTestCase("O", 11f, new float[] { 7f }, new float[] { 13f }),
+
+ new HintingTestCase("H", 13f, new float[] { 8f }, new float[] { 14f }),
+ new HintingTestCase("O", 13f, new float[] { 9f }, new float[] { 15f }),
+
+ new HintingTestCase("HO", 11f, new float[] { 7f, 7f }, new float[] { 13f, 13f }),
+ new HintingTestCase("OH", 11f, new float[] { 7f, 7f }, new float[] { 13f, 13f }),
+
+ new HintingTestCase("HO", 13f, new float[] { 8f, 9f }, new float[] { 14f, 15f }),
+ new HintingTestCase("OH", 13f, new float[] { 9f, 8f }, new float[] { 15f, 14f }),
+ };
+
+ @SmallTest
+ public void testHintingWidth() {
+ final Typeface fontTypeface = Typeface.createFromAsset(
+ getInstrumentation().getContext().getAssets(), FONT_PATH);
+ Paint paint = new Paint();
+ paint.setTypeface(fontTypeface);
+
+ for (int i = 0; i < HINTING_TESTCASES.length; ++i) {
+ HintingTestCase testCase = HINTING_TESTCASES[i];
+
+ paint.setTextSize(testCase.mTextSize);
+
+ float[] widths = new float[testCase.mText.length()];
+
+ paint.setHinting(Paint.HINTING_OFF);
+ paint.getTextWidths(String.valueOf(testCase.mText), widths);
+ assertEquals("Text width of '" + testCase.mText + "' without hinting is not expected.",
+ testCase.mWidthWithoutHinting, widths);
+
+ paint.setHinting(Paint.HINTING_ON);
+ paint.getTextWidths(String.valueOf(testCase.mText), widths);
+ assertEquals("Text width of '" + testCase.mText + "' with hinting is not expected.",
+ testCase.mWidthWithHinting, widths);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/text/method/WordIteratorTest.java b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
new file mode 100644
index 0000000..37f887c
--- /dev/null
+++ b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.method;
+
+import android.test.AndroidTestCase;
+
+import java.text.BreakIterator;
+import java.util.Locale;
+
+// TODO(Bug: 24062099): Add more tests for non-ascii text.
+public class WordIteratorTest extends AndroidTestCase {
+
+ public void testSetCharSequence() {
+ final String text = "text";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+
+ try {
+ wordIterator.setCharSequence(text, 100, 100);
+ fail("setCharSequence with invalid start and end values should throw "
+ + "IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ try {
+ wordIterator.setCharSequence(text, -100, -100);
+ fail("setCharSequence with invalid start and end values should throw "
+ + "IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ wordIterator.setCharSequence(text, 0, text.length());
+ wordIterator.setCharSequence(text, 0, 0);
+ wordIterator.setCharSequence(text, text.length(), text.length());
+ }
+
+ public void testPreceding() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.preceding(-1);
+ fail("preceding with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.preceding(text.length() + 1);
+ fail("preceding with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertEquals(BreakIterator.DONE, wordIterator.preceding(text.indexOf('a')));
+ assertEquals(text.indexOf('a'), wordIterator.preceding(text.indexOf('c')));
+ assertEquals(text.indexOf('a'), wordIterator.preceding(text.indexOf('d')));
+ assertEquals(text.indexOf('d'), wordIterator.preceding(text.indexOf('e')));
+ assertEquals(text.indexOf('d'), wordIterator.preceding(text.indexOf('g')));
+ assertEquals(text.indexOf('g'), wordIterator.preceding(text.indexOf('h')));
+ assertEquals(text.indexOf('g'), wordIterator.preceding(text.indexOf('j')));
+ assertEquals(text.indexOf('j'), wordIterator.preceding(text.indexOf('l')));
+ }
+
+ public void testFollowing() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.following(-1);
+ fail("following with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.following(text.length() + 1);
+ fail("following with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertEquals(text.indexOf('c') + 1, wordIterator.following(text.indexOf('a')));
+ assertEquals(text.indexOf('c') + 1, wordIterator.following(text.indexOf('c')));
+ assertEquals(text.indexOf('f') + 1, wordIterator.following(text.indexOf('c') + 1));
+ assertEquals(text.indexOf('f') + 1, wordIterator.following(text.indexOf('d')));
+ assertEquals(text.indexOf('i') + 1, wordIterator.following(text.indexOf('-')));
+ assertEquals(text.indexOf('i') + 1, wordIterator.following(text.indexOf('g')));
+ assertEquals(text.length(), wordIterator.following(text.indexOf('j')));
+ assertEquals(BreakIterator.DONE, wordIterator.following(text.length()));
+ }
+
+ public void testIsBoundary() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.isBoundary(-1);
+ fail("isBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.isBoundary(text.length() + 1);
+ fail("isBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertTrue(wordIterator.isBoundary(text.indexOf('a')));
+ assertFalse(wordIterator.isBoundary(text.indexOf('b')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('c') + 1));
+ assertTrue(wordIterator.isBoundary(text.indexOf('d')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('-')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('g')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('.')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('j')));
+ assertTrue(wordIterator.isBoundary(text.length()));
+ }
+
+ public void testNextBoundary() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.nextBoundary(-1);
+ fail("nextBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.nextBoundary(text.length() + 1);
+ fail("nextBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+
+ int currentOffset = 0;
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('c') + 1, currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('d'), currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('f') + 1, currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('g'), currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('i') + 1, currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('.') + 1, currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('j'), currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.length(), currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(BreakIterator.DONE, currentOffset);
+ }
+
+ public void testPrevBoundary() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.prevBoundary(-1);
+ fail("prevBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.prevBoundary(text.length() + 1);
+ fail("prevBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ int currentOffset = text.length();
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('j'), currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('.') + 1, currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('i') + 1, currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('g'), currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('f') + 1, currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('d'), currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('c') + 1, currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('a'), currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(BreakIterator.DONE, currentOffset);
+ }
+
+ public void testGetBeginning() {
+ {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+ try {
+ wordIterator.getBeginning(-1);
+ fail("getBeginning with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getBeginning(text.length() + 1);
+ fail("getBeginning with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(-1);
+ fail("getPrevWordBeginningOnTwoWordsBoundary with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.length() + 1);
+ fail("getPrevWordBeginningOnTwoWordsBoundary with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertEquals(text.indexOf('a'), wordIterator.getBeginning(text.indexOf('a')));
+ assertEquals(text.indexOf('a'), wordIterator.getBeginning(text.indexOf('c')));
+ assertEquals(text.indexOf('a'), wordIterator.getBeginning(text.indexOf('c') + 1));
+ assertEquals(text.indexOf('d'), wordIterator.getBeginning(text.indexOf('d')));
+ assertEquals(text.indexOf('d'), wordIterator.getBeginning(text.indexOf('-')));
+ assertEquals(text.indexOf('g'), wordIterator.getBeginning(text.indexOf('g')));
+ assertEquals(text.indexOf('g'), wordIterator.getBeginning(text.indexOf('.')));
+ assertEquals(BreakIterator.DONE, wordIterator.getBeginning(text.indexOf('.') + 1));
+ assertEquals(text.indexOf('j'), wordIterator.getBeginning(text.indexOf('j')));
+ assertEquals(text.indexOf('j'), wordIterator.getBeginning(text.indexOf('l') + 1));
+
+ for (int i = 0; i < text.length(); i++) {
+ assertEquals(wordIterator.getBeginning(i),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(i));
+ }
+ }
+
+ {
+ // Japanese HIRAGANA letter + KATAKANA letters
+ final String text = "\u3042\u30A2\u30A3\u30A4";
+ WordIterator wordIterator = new WordIterator(Locale.JAPANESE);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertEquals(text.indexOf('\u3042'), wordIterator.getBeginning(text.indexOf('\u3042')));
+ assertEquals(text.indexOf('\u30A2'), wordIterator.getBeginning(text.indexOf('\u30A2')));
+ assertEquals(text.indexOf('\u30A2'), wordIterator.getBeginning(text.indexOf('\u30A4')));
+ assertEquals(text.indexOf('\u30A2'), wordIterator.getBeginning(text.length()));
+
+ assertEquals(text.indexOf('\u3042'),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u3042')));
+ assertEquals(text.indexOf('\u3042'),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u30A2')));
+ assertEquals(text.indexOf('\u30A2'),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u30A4')));
+ assertEquals(text.indexOf('\u30A2'),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.length()));
+ }
+ }
+
+ public void testGetEnd() {
+ {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+ try {
+ wordIterator.getEnd(-1);
+ fail("getEnd with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getEnd(text.length() + 1);
+ fail("getEnd with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getNextWordEndOnTwoWordBoundary(-1);
+ fail("getNextWordEndOnTwoWordBoundary with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.length() + 1);
+ fail("getNextWordEndOnTwoWordBoundary with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertEquals(text.indexOf('c') + 1, wordIterator.getEnd(text.indexOf('a')));
+ assertEquals(text.indexOf('c') + 1, wordIterator.getEnd(text.indexOf('c')));
+ assertEquals(text.indexOf('c') + 1, wordIterator.getEnd(text.indexOf('c') + 1));
+ assertEquals(text.indexOf('f') + 1, wordIterator.getEnd(text.indexOf('d')));
+ assertEquals(text.indexOf('f') + 1, wordIterator.getEnd(text.indexOf('f') + 1));
+ assertEquals(text.indexOf('i') + 1, wordIterator.getEnd(text.indexOf('g')));
+ assertEquals(text.indexOf('i') + 1, wordIterator.getEnd(text.indexOf('i') + 1));
+ assertEquals(BreakIterator.DONE, wordIterator.getEnd(text.indexOf('.') + 1));
+ assertEquals(text.indexOf('l') + 1, wordIterator.getEnd(text.indexOf('j')));
+ assertEquals(text.indexOf('l') + 1, wordIterator.getEnd(text.indexOf('l') + 1));
+
+ for (int i = 0; i < text.length(); i++) {
+ assertEquals(wordIterator.getEnd(i),
+ wordIterator.getNextWordEndOnTwoWordBoundary(i));
+ }
+ }
+
+ {
+ // Japanese HIRAGANA letter + KATAKANA letters
+ final String text = "\u3042\u30A2\u30A3\u30A4";
+ WordIterator wordIterator = new WordIterator(Locale.JAPANESE);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertEquals(text.indexOf('\u3042') + 1, wordIterator.getEnd(text.indexOf('\u3042')));
+ assertEquals(text.indexOf('\u3042') + 1, wordIterator.getEnd(text.indexOf('\u30A2')));
+ assertEquals(text.indexOf('\u30A4') + 1, wordIterator.getEnd(text.indexOf('\u30A4')));
+ assertEquals(text.indexOf('\u30A4') + 1,
+ wordIterator.getEnd(text.indexOf('\u30A4') + 1));
+
+ assertEquals(text.indexOf('\u3042') + 1,
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u3042')));
+ assertEquals(text.indexOf('\u30A4') + 1,
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A2')));
+ assertEquals(text.indexOf('\u30A4') + 1,
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A4')));
+ assertEquals(text.indexOf('\u30A4') + 1,
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A4') + 1));
+ }
+ }
+
+ public void testGetPunctuationBeginning() {
+ final String text = "abc!? (^^;) def";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ // TODO: Shouldn't this throw an exception?
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationBeginning(BreakIterator.DONE));
+
+ try {
+ wordIterator.getPunctuationBeginning(-2);
+ fail("getPunctuationBeginning with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getPunctuationBeginning(text.length() + 1);
+ fail("getPunctuationBeginning with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationBeginning(text.indexOf('a')));
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationBeginning(text.indexOf('c')));
+ assertEquals(text.indexOf('!'), wordIterator.getPunctuationBeginning(text.indexOf('!')));
+ assertEquals(text.indexOf('!'),
+ wordIterator.getPunctuationBeginning(text.indexOf('?') + 1));
+ assertEquals(text.indexOf(';'), wordIterator.getPunctuationBeginning(text.indexOf(';')));
+ assertEquals(text.indexOf(';'), wordIterator.getPunctuationBeginning(text.indexOf(')')));
+ assertEquals(text.indexOf(';'), wordIterator.getPunctuationBeginning(text.length()));
+ }
+
+ public void testGetPunctuationEnd() {
+ final String text = "abc!? (^^;) def";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ // TODO: Shouldn't this throw an exception?
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationEnd(BreakIterator.DONE));
+
+ try {
+ wordIterator.getPunctuationEnd(-2);
+ fail("getPunctuationEnd with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getPunctuationEnd(text.length() + 1);
+ fail("getPunctuationBeginning with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertEquals(text.indexOf('?') + 1, wordIterator.getPunctuationEnd(text.indexOf('a')));
+ assertEquals(text.indexOf('?') + 1, wordIterator.getPunctuationEnd(text.indexOf('?') + 1));
+ assertEquals(text.indexOf('(') + 1, wordIterator.getPunctuationEnd(text.indexOf('(')));
+ assertEquals(text.indexOf(')') + 1, wordIterator.getPunctuationEnd(text.indexOf('(') + 2));
+ assertEquals(text.indexOf(')') + 1, wordIterator.getPunctuationEnd(text.indexOf(')') + 1));
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationEnd(text.indexOf('d')));
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationEnd(text.length()));
+ }
+
+ public void testIsAfterPunctuation() {
+ final String text = "abc!? (^^;) def";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertFalse(wordIterator.isAfterPunctuation(text.indexOf('a')));
+ assertFalse(wordIterator.isAfterPunctuation(text.indexOf('!')));
+ assertTrue(wordIterator.isAfterPunctuation(text.indexOf('?')));
+ assertTrue(wordIterator.isAfterPunctuation(text.indexOf('?') + 1));
+ assertFalse(wordIterator.isAfterPunctuation(text.indexOf('d')));
+
+ assertFalse(wordIterator.isAfterPunctuation(BreakIterator.DONE));
+ assertFalse(wordIterator.isAfterPunctuation(text.length() + 1));
+ }
+
+ public void testIsOnPunctuation() {
+ final String text = "abc!? (^^;) def";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertFalse(wordIterator.isOnPunctuation(text.indexOf('a')));
+ assertTrue(wordIterator.isOnPunctuation(text.indexOf('!')));
+ assertTrue(wordIterator.isOnPunctuation(text.indexOf('?')));
+ assertFalse(wordIterator.isOnPunctuation(text.indexOf('?') + 1));
+ assertTrue(wordIterator.isOnPunctuation(text.indexOf(')')));
+ assertFalse(wordIterator.isOnPunctuation(text.indexOf(')') + 1));
+ assertFalse(wordIterator.isOnPunctuation(text.indexOf('d')));
+
+ assertFalse(wordIterator.isOnPunctuation(BreakIterator.DONE));
+ assertFalse(wordIterator.isOnPunctuation(text.length()));
+ assertFalse(wordIterator.isOnPunctuation(text.length() + 1));
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index e5d5542..87b3785 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -38,15 +38,22 @@
}
@SmallTest
- public void testTextAndAppearanceInSuggestionsPopup() {
+ public void testTextAppearanceInSuggestionsPopup() {
final Activity activity = getActivity();
final String sampleText = "abc def ghi";
- final String[] candidate = {"DEF", "Def"};
- final SuggestionSpan suggestionSpan = new SuggestionSpan(activity, candidate,
- SuggestionSpan.FLAG_AUTO_CORRECTION);
- final int spanStart = 4;
- final int spanEnd = 7;
+ final String[] singleWordCandidates = {"DEF", "Def"};
+ final SuggestionSpan singleWordSuggestionSpan = new SuggestionSpan(activity,
+ singleWordCandidates, SuggestionSpan.FLAG_AUTO_CORRECTION);
+ final int singleWordSpanStart = 4;
+ final int singleWordSpanEnd = 7;
+
+ final String[] multiWordCandidates = {"ABC DEF GHI", "Abc Def Ghi"};
+ final SuggestionSpan multiWordSuggestionSpan = new SuggestionSpan(activity,
+ multiWordCandidates, SuggestionSpan.FLAG_AUTO_CORRECTION);
+ final int multiWordSpanStart = 0;
+ final int multiWordSpanEnd = 11;
+
TextAppearanceSpan expectedSpan = new TextAppearanceSpan(activity,
android.R.style.TextAppearance_SuggestionHighlight);
TextPaint tmpTp = new TextPaint();
@@ -62,22 +69,27 @@
public void run() {
SpannableStringBuilder ssb = new SpannableStringBuilder();
ssb.append(sampleText);
- ssb.setSpan(suggestionSpan, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ ssb.setSpan(singleWordSuggestionSpan, singleWordSpanStart, singleWordSpanEnd,
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ ssb.setSpan(multiWordSuggestionSpan, multiWordSpanStart, multiWordSpanEnd,
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
editText.setText(ssb);
- Selection.setSelection(editText.getText(), spanStart, spanEnd);
+ Selection.setSelection(editText.getText(), singleWordSpanStart, singleWordSpanEnd);
editText.onTextContextMenuItem(TextView.ID_REPLACE);
}
});
getInstrumentation().waitForIdleSync();
// In this test, the SuggestionsPopupWindow looks like
- // abc def ghi
- // ----------
- // | *DEF* |
- // | *Def* |
- // | DELETE |
- // ----------
+ // abc def ghi
+ // -----------------
+ // | abc *DEF* ghi |
+ // | abc *Def* ghi |
+ // | *ABC DEF GHI* |
+ // | *Abc Def Ghi* |
+ // | DELETE |
+ // -----------------
// *XX* means that XX is highlighted.
activity.runOnUiThread(new Runnable() {
public void run() {
@@ -90,9 +102,10 @@
int childNum = listView.getChildCount();
// +1 for "DELETE" command.
- assertEquals(candidate.length + 1, childNum);
+ assertEquals(singleWordCandidates.length + multiWordCandidates.length + 1,
+ childNum);
- for (int i = 0; i < candidate.length; ++i) {
+ for (int i = 0; i < singleWordCandidates.length; ++i) {
TextView textView = (TextView) listView.getChildAt(i);
assertNotNull(textView);
@@ -100,16 +113,46 @@
assertNotNull(spanned);
// Check that the suggestion item order is kept.
- assertEquals(candidate[i], spanned.toString());
+ String expectedText = "abc " + singleWordCandidates[i] + " ghi";
+ assertEquals(expectedText, spanned.toString());
// Check that the text is highlighted with correct color and text size.
- TextAppearanceSpan[] taSpan = spanned.getSpans(0, candidate[i].length(),
- TextAppearanceSpan.class);
+ TextAppearanceSpan[] taSpan = spanned.getSpans(singleWordSpanStart,
+ singleWordSpanEnd, TextAppearanceSpan.class);
assertEquals(1, taSpan.length);
TextPaint tp = new TextPaint();
taSpan[0].updateDrawState(tp);
assertEquals(expectedHighlightTextColor, tp.getColor());
assertEquals(expectedHighlightTextSize, tp.getTextSize());
+
+ // Check only center word is highlighted.
+ assertEquals(singleWordSpanStart, spanned.getSpanStart(taSpan[0]));
+ assertEquals(singleWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
+ }
+
+ for (int i = 0; i < multiWordCandidates.length; ++i) {
+ int indexInListView = singleWordCandidates.length + i;
+ TextView textView = (TextView) listView.getChildAt(indexInListView);
+ assertNotNull(textView);
+
+ Spanned spanned = (Spanned) textView.getText();
+ assertNotNull(spanned);
+
+ // Check that the suggestion item order is kept.
+ assertEquals(multiWordCandidates[i], spanned.toString());
+
+ // Check that the text is highlighted with correct color and text size.
+ TextAppearanceSpan[] taSpan = spanned.getSpans(
+ 0, multiWordCandidates[i].length(), TextAppearanceSpan.class);
+ assertEquals(1, taSpan.length);
+ TextPaint tp = new TextPaint();
+ taSpan[0].updateDrawState(tp);
+ assertEquals(expectedHighlightTextColor, tp.getColor());
+ assertEquals(expectedHighlightTextSize, tp.getTextSize());
+
+ // Check the whole text is highlighted.
+ assertEquals(multiWordSpanStart, spanned.getSpanStart(taSpan[0]));
+ assertEquals(multiWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
}
}
});
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 54117df..4db1d9a 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,8 +16,17 @@
package android.widget;
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
+import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
+import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
+import static android.widget.espresso.TextViewActions.longPressAndDragOnText;
+import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
+import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
+import static android.widget.espresso.TextViewAssertions.hasSelection;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.pressKey;
import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
@@ -27,6 +36,7 @@
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.view.KeyEvent;
/**
* Tests the TextView widget from an Activity
@@ -47,4 +57,117 @@
onView(withId(R.id.textview)).check(matches(withText(helloWorld)));
}
+
+ @SmallTest
+ public void testPositionCursorAtTextAtIndex() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello world!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
+
+ // Delete text at specified index and see if we got the right one.
+ onView(withId(R.id.textview)).perform(pressKey(KeyEvent.KEYCODE_FORWARD_DEL));
+ onView(withId(R.id.textview)).check(matches(withText("Hello orld!")));
+ }
+
+ @SmallTest
+ public void testLongPressToSelect() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello Kirk!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ longPressOnTextAtIndex(helloWorld.indexOf("Kirk")));
+
+ onView(withId(R.id.textview)).check(hasSelection("Kirk"));
+ }
+
+ @SmallTest
+ public void testLongPressEmptySpace() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello big round sun!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ // Move cursor somewhere else
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("big")));
+ // Long-press at end of line.
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(helloWorld.length()));
+
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(helloWorld.length()));
+ }
+
+ @SmallTest
+ public void testLongPressAndDragToSelect() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello little handsome boy!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!")));
+
+ onView(withId(R.id.textview)).check(hasSelection("little handsome"));
+ }
+
+ @SmallTest
+ public void testDoubleTapToSelect() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello SuetYi!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ doubleClickOnTextAtIndex(helloWorld.indexOf("SuetYi")));
+
+ onView(withId(R.id.textview)).check(hasSelection("SuetYi"));
+ }
+
+ @SmallTest
+ public void testDoubleTapAndDragToSelect() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello young beautiful girl!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ doubleTapAndDragOnText(helloWorld.indexOf("young"), helloWorld.indexOf(" girl!")));
+
+ onView(withId(R.id.textview)).check(hasSelection("young beautiful"));
+ }
+
+ @SmallTest
+ public void testSelectBackwordsByTouch() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello king of the Jungle!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ doubleTapAndDragOnText(helloWorld.indexOf(" Jungle!"), helloWorld.indexOf("king")));
+
+ onView(withId(R.id.textview)).check(hasSelection("king of the"));
+ }
+
+ @SmallTest
+ public void testToolbarAppearsAfterSelection() throws Exception {
+ getActivity();
+
+ // It'll be nice to check that the toolbar is not visible (or does not exist) here
+ // I can't currently find a way to do this. I'll get to it later.
+
+ final String text = "Toolbar appears after selection.";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+ onView(withId(R.id.textview)).perform(
+ longPressOnTextAtIndex(text.indexOf("appears")));
+
+ // It takes the toolbar less than 100ms to start to animate into screen.
+ // Ideally, we'll wait using the UiController, but I guess this works for now.
+ Thread.sleep(100);
+ assertFloatingToolbarIsDisplayed(getActivity());
+ }
}
diff --git a/core/tests/coretests/src/android/widget/TextViewWordLimitsTest.java b/core/tests/coretests/src/android/widget/TextViewWordLimitsTest.java
deleted file mode 100644
index 4b66164..0000000
--- a/core/tests/coretests/src/android/widget/TextViewWordLimitsTest.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-
-package android.widget;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.text.InputType;
-import android.text.Selection;
-import android.text.Spannable;
-import android.text.SpannableString;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * TextViewPatchTest tests {@link TextView}'s definition of word. Finds and
- * verifies word limits to be in strings containing different kinds of
- * characters.
- */
-@Suppress // Failing.
-public class TextViewWordLimitsTest extends AndroidTestCase {
-
- TextView mTv = null;
- Method mGetWordLimits, mSelectCurrentWord;
- Field mContextMenuTriggeredByKey, mSelectionControllerEnabled;
-
-
- /**
- * Sets up common fields used in all test cases.
- * @throws NoSuchFieldException
- * @throws SecurityException
- */
- @Override
- protected void setUp() throws NoSuchMethodException, SecurityException, NoSuchFieldException {
- mTv = new TextView(getContext());
- mTv.setInputType(InputType.TYPE_CLASS_TEXT);
-
- mGetWordLimits = mTv.getClass().getDeclaredMethod("getWordLimitsAt",
- new Class[] {int.class});
- mGetWordLimits.setAccessible(true);
-
- mSelectCurrentWord = mTv.getClass().getDeclaredMethod("selectCurrentWord", new Class[] {});
- mSelectCurrentWord.setAccessible(true);
-
- mContextMenuTriggeredByKey = mTv.getClass().getDeclaredField("mContextMenuTriggeredByKey");
- mContextMenuTriggeredByKey.setAccessible(true);
- mSelectionControllerEnabled = mTv.getClass().getDeclaredField("mSelectionControllerEnabled");
- mSelectionControllerEnabled.setAccessible(true);
- }
-
- /**
- * Calculate and verify word limits. Depends on the TextView implementation.
- * Uses a private method and internal data representation.
- *
- * @param text Text to select a word from
- * @param pos Position to expand word around
- * @param correctStart Correct start position for the word
- * @param correctEnd Correct end position for the word
- * @throws InvocationTargetException
- * @throws IllegalAccessException
- * @throws IllegalArgumentException
- * @throws InvocationTargetException
- * @throws IllegalAccessException
- */
- private void verifyWordLimits(String text, int pos, int correctStart, int correctEnd)
- throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
- mTv.setText(text, TextView.BufferType.SPANNABLE);
-
- long limits = (Long)mGetWordLimits.invoke(mTv, new Object[] {new Integer(pos)});
- int actualStart = (int)(limits >>> 32);
- int actualEnd = (int)(limits & 0x00000000FFFFFFFFL);
- assertEquals(correctStart, actualStart);
- assertEquals(correctEnd, actualEnd);
- }
-
-
- private void verifySelectCurrentWord(Spannable text, int selectionStart, int selectionEnd, int correctStart,
- int correctEnd) throws InvocationTargetException, IllegalAccessException {
- mTv.setText(text, TextView.BufferType.SPANNABLE);
-
- Selection.setSelection((Spannable)mTv.getText(), selectionStart, selectionEnd);
- mContextMenuTriggeredByKey.setBoolean(mTv, true);
- mSelectionControllerEnabled.setBoolean(mTv, true);
- mSelectCurrentWord.invoke(mTv);
-
- assertEquals(correctStart, mTv.getSelectionStart());
- assertEquals(correctEnd, mTv.getSelectionEnd());
- }
-
-
- /**
- * Corner cases for string length.
- */
- @LargeTest
- public void testLengths() throws Exception {
- final String ONE_TWO = "one two";
- final String EMPTY = "";
- final String TOOLONG = "ThisWordIsTooLongToBeDefinedAsAWordInTheSenseUsedInTextView";
-
- // Select first word
- verifyWordLimits(ONE_TWO, 0, 0, 3);
- verifyWordLimits(ONE_TWO, 3, 0, 3);
-
- // Select last word
- verifyWordLimits(ONE_TWO, 4, 4, 7);
- verifyWordLimits(ONE_TWO, 7, 4, 7);
-
- // Empty string
- verifyWordLimits(EMPTY, 0, -1, -1);
-
- // Too long word
- verifyWordLimits(TOOLONG, 0, -1, -1);
- }
-
- /**
- * Unicode classes included.
- */
- @LargeTest
- public void testIncludedClasses() throws Exception {
- final String LOWER = "njlj";
- final String UPPER = "NJLJ";
- final String TITLECASE = "\u01C8\u01CB\u01F2"; // Lj Nj Dz
- final String OTHER = "\u3042\u3044\u3046"; // Hiragana AIU
- final String MODIFIER = "\u02C6\u02CA\u02CB"; // Circumflex Acute Grave
-
- // Each string contains a single valid word
- verifyWordLimits(LOWER, 1, 0, 4);
- verifyWordLimits(UPPER, 1, 0, 4);
- verifyWordLimits(TITLECASE, 1, 0, 3);
- verifyWordLimits(OTHER, 1, 0, 3);
- verifyWordLimits(MODIFIER, 1, 0, 3);
- }
-
- /**
- * Unicode classes included if combined with a letter.
- */
- @LargeTest
- public void testPartlyIncluded() throws Exception {
- final String NUMBER = "123";
- final String NUMBER_LOWER = "1st";
- final String APOSTROPHE = "''";
- final String APOSTROPHE_LOWER = "'Android's'";
-
- // Pure decimal number is ignored
- verifyWordLimits(NUMBER, 1, -1, -1);
-
- // Number with letter is valid
- verifyWordLimits(NUMBER_LOWER, 1, 0, 3);
-
- // Stand apostrophes are ignore
- verifyWordLimits(APOSTROPHE, 1, -1, -1);
-
- // Apostrophes are accepted if they are a part of a word
- verifyWordLimits(APOSTROPHE_LOWER, 1, 0, 11);
- }
-
- /**
- * Unicode classes included if combined with a letter.
- */
- @LargeTest
- public void testFinalSeparator() throws Exception {
- final String PUNCTUATION = "abc, def.";
-
- // Starting from the comma
- verifyWordLimits(PUNCTUATION, 3, 0, 3);
- verifyWordLimits(PUNCTUATION, 4, 0, 4);
-
- // Starting from the final period
- verifyWordLimits(PUNCTUATION, 8, 5, 8);
- verifyWordLimits(PUNCTUATION, 9, 5, 9);
- }
-
- /**
- * Unicode classes other than listed in testIncludedClasses and
- * testPartlyIncluded act as word separators.
- */
- @LargeTest
- public void testNotIncluded() throws Exception {
- // Selection of character classes excluded
- final String MARK_NONSPACING = "a\u030A"; // a Combining ring above
- final String PUNCTUATION_OPEN_CLOSE = "(c)"; // Parenthesis
- final String PUNCTUATION_DASH = "non-fiction"; // Hyphen
- final String PUNCTUATION_OTHER = "b&b"; // Ampersand
- final String SYMBOL_OTHER = "Android\u00AE"; // Registered
- final String SEPARATOR_SPACE = "one two"; // Space
-
- // "a"
- verifyWordLimits(MARK_NONSPACING, 1, 0, 1);
-
- // "c"
- verifyWordLimits(PUNCTUATION_OPEN_CLOSE, 1, 1, 2);
-
- // "non-"
- verifyWordLimits(PUNCTUATION_DASH, 3, 0, 3);
- verifyWordLimits(PUNCTUATION_DASH, 4, 4, 11);
-
- // "b"
- verifyWordLimits(PUNCTUATION_OTHER, 0, 0, 1);
- verifyWordLimits(PUNCTUATION_OTHER, 1, 0, 1);
- verifyWordLimits(PUNCTUATION_OTHER, 2, 0, 3); // & is considered a punctuation sign.
- verifyWordLimits(PUNCTUATION_OTHER, 3, 2, 3);
-
- // "Android"
- verifyWordLimits(SYMBOL_OTHER, 7, 0, 7);
- verifyWordLimits(SYMBOL_OTHER, 8, -1, -1);
-
- // "one"
- verifyWordLimits(SEPARATOR_SPACE, 1, 0, 3);
- }
-
- /**
- * Surrogate characters are treated as their code points.
- */
- @LargeTest
- public void testSurrogate() throws Exception {
- final String SURROGATE_LETTER = "\uD800\uDC00\uD800\uDC01\uD800\uDC02"; // Linear B AEI
- final String SURROGATE_SYMBOL = "\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03"; // Three smileys
-
- // Letter Other is included even when coded as surrogate pairs
- verifyWordLimits(SURROGATE_LETTER, 0, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 1, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 2, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 3, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 4, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 5, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 6, 0, 6);
-
- // Not included classes are ignored even when coded as surrogate pairs
- verifyWordLimits(SURROGATE_SYMBOL, 0, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 1, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 2, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 3, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 4, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 5, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 6, -1, -1);
- }
-
- /**
- * Selection is used if present and valid word.
- */
- @LargeTest
- public void testSelectCurrentWord() throws Exception {
- SpannableString textLower = new SpannableString("first second");
- SpannableString textOther = new SpannableString("\u3042\3044\3046"); // Hiragana AIU
- SpannableString textDash = new SpannableString("non-fiction"); // Hyphen
- SpannableString textPunctOther = new SpannableString("b&b"); // Ampersand
- SpannableString textSymbolOther = new SpannableString("Android\u00AE"); // Registered
-
- // Valid selection - Letter, Lower
- verifySelectCurrentWord(textLower, 2, 5, 0, 5);
-
- // Adding the space spreads to the second word
- verifySelectCurrentWord(textLower, 2, 6, 0, 12);
-
- // Valid selection -- Letter, Other
- verifySelectCurrentWord(textOther, 1, 2, 0, 5);
-
- // Zero-width selection is interpreted as a cursor and the selection is ignored
- verifySelectCurrentWord(textLower, 2, 2, 0, 5);
-
- // Hyphen is part of selection
- verifySelectCurrentWord(textDash, 2, 5, 0, 11);
-
- // Ampersand part of selection or not
- verifySelectCurrentWord(textPunctOther, 1, 2, 0, 3);
- verifySelectCurrentWord(textPunctOther, 1, 3, 0, 3);
-
- // (R) part of the selection
- verifySelectCurrentWord(textSymbolOther, 2, 7, 0, 7);
- verifySelectCurrentWord(textSymbolOther, 2, 8, 0, 8);
- }
-}
diff --git a/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
new file mode 100644
index 0000000..a0cd848
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static org.hamcrest.Matchers.allOf;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.PerformException;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.CoordinatesProvider;
+import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.MotionEvents;
+import android.support.test.espresso.action.PrecisionDescriber;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Swiper;
+import android.support.test.espresso.action.Tap;
+import android.support.test.espresso.util.HumanReadables;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.TextView;
+import org.hamcrest.Matcher;
+
+
+/**
+ * Drags on text in a TextView using touch events.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ */
+public final class DragOnTextViewActions implements ViewAction {
+
+ /**
+ * Executes different "drag on text" types to given positions.
+ */
+ public enum Drag implements Swiper {
+
+ /**
+ * Starts a drag with a long-press.
+ */
+ LONG_PRESS {
+ private DownMotionPerformer downMotion = new DownMotionPerformer() {
+ @Override
+ public MotionEvent perform(
+ UiController uiController, float[] coordinates, float[] precision) {
+ MotionEvent downEvent = MotionEvents.sendDown(
+ uiController, coordinates, precision)
+ .down;
+ // Duration before a press turns into a long press.
+ // Factor 1.5 is needed, otherwise a long press is not safely detected.
+ // See android.test.TouchUtils longClickView
+ long longPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
+ uiController.loopMainThreadForAtLeast(longPressTimeout);
+ return downEvent;
+ }
+ };
+
+ @Override
+ public Status sendSwipe(
+ UiController uiController,
+ float[] startCoordinates, float[] endCoordinates, float[] precision) {
+ return sendLinearDrag(
+ uiController, downMotion, startCoordinates, endCoordinates, precision);
+ }
+
+ @Override
+ public String toString() {
+ return "long press and drag to select";
+ }
+ },
+
+ /**
+ * Starts a drag with a double-tap.
+ */
+ DOUBLE_TAP {
+ private DownMotionPerformer downMotion = new DownMotionPerformer() {
+ @Override
+ @Nullable
+ public MotionEvent perform(
+ UiController uiController, float[] coordinates, float[] precision) {
+ MotionEvent downEvent = MotionEvents.sendDown(
+ uiController, coordinates, precision)
+ .down;
+ try {
+ if (!MotionEvents.sendUp(uiController, downEvent)) {
+ String logMessage = "Injection of up event as part of the double tap " +
+ "failed. Sending cancel event.";
+ Log.d(TAG, logMessage);
+ MotionEvents.sendCancel(uiController, downEvent);
+ return null;
+ }
+
+ long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+ uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout);
+
+ return MotionEvents.sendDown(uiController, coordinates, precision).down;
+ } finally {
+ downEvent.recycle();
+ }
+ }
+ };
+
+ @Override
+ public Status sendSwipe(
+ UiController uiController,
+ float[] startCoordinates, float[] endCoordinates, float[] precision) {
+ return sendLinearDrag(
+ uiController, downMotion, startCoordinates, endCoordinates, precision);
+ }
+
+ @Override
+ public String toString() {
+ return "double-tap and drag to select";
+ }
+ };
+
+ private static final String TAG = Drag.class.getSimpleName();
+
+ /** The number of move events to send for each drag. */
+ private static final int DRAG_STEP_COUNT = 10;
+
+ /** Length of time a drag should last for, in milliseconds. */
+ private static final int DRAG_DURATION = 1500;
+
+ private static Status sendLinearDrag(
+ UiController uiController, DownMotionPerformer downMotion,
+ float[] startCoordinates, float[] endCoordinates, float[] precision) {
+ float[][] steps = interpolate(startCoordinates, endCoordinates);
+ final int delayBetweenMovements = DRAG_DURATION / steps.length;
+
+ MotionEvent downEvent = downMotion.perform(uiController, startCoordinates, precision);
+ if (downEvent == null) {
+ return Status.FAILURE;
+ }
+
+ try {
+ for (int i = 0; i < steps.length; i++) {
+ if (!MotionEvents.sendMovement(uiController, downEvent, steps[i])) {
+ String logMessage = "Injection of move event as part of the drag failed. " +
+ "Sending cancel event.";
+ Log.e(TAG, logMessage);
+ MotionEvents.sendCancel(uiController, downEvent);
+ return Status.FAILURE;
+ }
+
+ long desiredTime = downEvent.getDownTime() + delayBetweenMovements * i;
+ long timeUntilDesired = desiredTime - SystemClock.uptimeMillis();
+ if (timeUntilDesired > 10) {
+ // If the wait time until the next event isn't long enough, skip the wait
+ // and execute the next event.
+ uiController.loopMainThreadForAtLeast(timeUntilDesired);
+ }
+ }
+
+ if (!MotionEvents.sendUp(uiController, downEvent, endCoordinates)) {
+ String logMessage = "Injection of up event as part of the drag failed. " +
+ "Sending cancel event.";
+ Log.e(TAG, logMessage);
+ MotionEvents.sendCancel(uiController, downEvent);
+ return Status.FAILURE;
+ }
+ } finally {
+ downEvent.recycle();
+ }
+ return Status.SUCCESS;
+ }
+
+ private static float[][] interpolate(float[] start, float[] end) {
+ float[][] res = new float[DRAG_STEP_COUNT][2];
+
+ for (int i = 1; i < DRAG_STEP_COUNT + 1; i++) {
+ res[i - 1][0] = start[0] + (end[0] - start[0]) * i / (DRAG_STEP_COUNT + 2f);
+ res[i - 1][1] = start[1] + (end[1] - start[1]) * i / (DRAG_STEP_COUNT + 2f);
+ }
+
+ return res;
+ }
+ }
+
+ /**
+ * Interface to implement different "down motion" types.
+ */
+ private interface DownMotionPerformer {
+ /**
+ * Performs and returns a down motion.
+ *
+ * @param uiController a UiController to use to send MotionEvents to the screen.
+ * @param coordinates a float[] with x and y values of center of the tap.
+ * @param precision a float[] with x and y values of precision of the tap.
+ * @return the down motion event or null if the down motion event failed.
+ */
+ @Nullable
+ MotionEvent perform(UiController uiController, float[] coordinates, float[] precision);
+ }
+
+ private final Swiper mDragger;
+ private final CoordinatesProvider mStartCoordinatesProvider;
+ private final CoordinatesProvider mEndCoordinatesProvider;
+ private final PrecisionDescriber mPrecisionDescriber;
+
+ public DragOnTextViewActions(
+ Swiper dragger,
+ CoordinatesProvider startCoordinatesProvider,
+ CoordinatesProvider endCoordinatesProvider,
+ PrecisionDescriber precisionDescriber) {
+ mDragger = checkNotNull(dragger);
+ mStartCoordinatesProvider = checkNotNull(startCoordinatesProvider);
+ mEndCoordinatesProvider = checkNotNull(endCoordinatesProvider);
+ mPrecisionDescriber = checkNotNull(precisionDescriber);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Matcher<View> getConstraints() {
+ return allOf(isCompletelyDisplayed(), isAssignableFrom(TextView.class));
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ checkNotNull(uiController);
+ checkNotNull(view);
+
+ float[] startCoordinates = mStartCoordinatesProvider.calculateCoordinates(view);
+ float[] endCoordinates = mEndCoordinatesProvider.calculateCoordinates(view);
+ float[] precision = mPrecisionDescriber.describePrecision();
+
+ Swiper.Status status;
+
+ try {
+ status = mDragger.sendSwipe(
+ uiController, startCoordinates, endCoordinates, precision);
+ } catch (RuntimeException re) {
+ throw new PerformException.Builder()
+ .withActionDescription(this.getDescription())
+ .withViewDescription(HumanReadables.describe(view))
+ .withCause(re)
+ .build();
+ }
+
+ int duration = ViewConfiguration.getPressedStateDuration();
+ // ensures that all work enqueued to process the swipe has been run.
+ if (duration > 0) {
+ uiController.loopMainThreadForAtLeast(duration);
+ }
+
+ if (status == Swiper.Status.FAILURE) {
+ throw new PerformException.Builder()
+ .withActionDescription(getDescription())
+ .withViewDescription(HumanReadables.describe(view))
+ .withCause(new RuntimeException(getDescription() + " failed"))
+ .build();
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return mDragger.toString();
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
new file mode 100644
index 0000000..fc01d84
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
+import android.app.Activity;
+import com.android.internal.widget.FloatingToolbar;
+
+/**
+ * Espresso utility methods for the floating toolbar.
+ */
+public class FloatingToolbarEspressoUtils {
+
+
+ private FloatingToolbarEspressoUtils() {}
+
+ /**
+ * Asserts that the floating toolbar is displayed on screen.
+ *
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertFloatingToolbarIsDisplayed(Activity activity) {
+ onView(withTagValue(is((Object) FloatingToolbar.FLOATING_TOOLBAR_TAG)))
+ .inRoot(withDecorView(not(is(activity.getWindow().getDecorView()))))
+ .check(matches(isDisplayed()));
+ }
+
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
new file mode 100644
index 0000000..835b1b9
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
+
+import android.support.test.espresso.PerformException;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.CoordinatesProvider;
+import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Tap;
+import android.support.test.espresso.util.HumanReadables;
+import android.text.Layout;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * A collection of actions on a {@link android.widget.TextView}.
+ */
+public final class TextViewActions {
+
+ private TextViewActions() {}
+
+ /**
+ * Returns an action that clicks on text at an index on the TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param index The index of the TextView's text to click on.
+ */
+ public static ViewAction clickOnTextAtIndex(int index) {
+ return actionWithAssertions(
+ new GeneralClickAction(Tap.SINGLE, new TextCoordinates(index), Press.FINGER));
+ }
+
+ /**
+ * Returns an action that double-clicks on text at an index on the TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param index The index of the TextView's text to double-click on.
+ */
+ public static ViewAction doubleClickOnTextAtIndex(int index) {
+ return actionWithAssertions(
+ new GeneralClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.FINGER));
+ }
+
+ /**
+ * Returns an action that long presses on text at an index on the TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param index The index of the TextView's text to long press on.
+ */
+ public static ViewAction longPressOnTextAtIndex(int index) {
+ return actionWithAssertions(
+ new GeneralClickAction(Tap.LONG, new TextCoordinates(index), Press.FINGER));
+ }
+
+ /**
+ * Returns an action that long presses then drags on text from startIndex to endIndex on the
+ * TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param startIndex The index of the TextView's text to start a drag from
+ * @param endIndex The index of the TextView's text to end the drag at
+ */
+ public static ViewAction longPressAndDragOnText(int startIndex, int endIndex) {
+ return actionWithAssertions(
+ new DragOnTextViewActions(
+ DragOnTextViewActions.Drag.LONG_PRESS,
+ new TextCoordinates(startIndex),
+ new TextCoordinates(endIndex),
+ Press.FINGER));
+ }
+
+ /**
+ * Returns an action that double taps then drags on text from startIndex to endIndex on the
+ * TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param startIndex The index of the TextView's text to start a drag from
+ * @param endIndex The index of the TextView's text to end the drag at
+ */
+ public static ViewAction doubleTapAndDragOnText(int startIndex, int endIndex) {
+ return actionWithAssertions(
+ new DragOnTextViewActions(
+ DragOnTextViewActions.Drag.DOUBLE_TAP,
+ new TextCoordinates(startIndex),
+ new TextCoordinates(endIndex),
+ Press.FINGER));
+ }
+
+ /**
+ * A provider of the x, y coordinates of the text at the specified index in a text view.
+ */
+ private static final class TextCoordinates implements CoordinatesProvider {
+
+ private final int mIndex;
+ private final String mActionDescription;
+
+ public TextCoordinates(int index) {
+ mIndex = index;
+ mActionDescription = "Could not locate text at index: " + mIndex;
+ }
+
+ @Override
+ public float[] calculateCoordinates(View view) {
+ try {
+ return locateTextAtIndex((TextView) view, mIndex);
+ } catch (ClassCastException e) {
+ throw new PerformException.Builder()
+ .withActionDescription(mActionDescription)
+ .withViewDescription(HumanReadables.describe(view))
+ .withCause(e)
+ .build();
+ } catch (StringIndexOutOfBoundsException e) {
+ throw new PerformException.Builder()
+ .withActionDescription(mActionDescription)
+ .withViewDescription(HumanReadables.describe(view))
+ .withCause(e)
+ .build();
+ }
+ }
+
+ /**
+ * @throws StringIndexOutOfBoundsException
+ */
+ private float[] locateTextAtIndex(TextView textView, int index) {
+ if (index < 0 || index > textView.getText().length()) {
+ throw new StringIndexOutOfBoundsException(index);
+ }
+ final int[] xy = new int[2];
+ textView.getLocationOnScreen(xy);
+ final Layout layout = textView.getLayout();
+ final int line = layout.getLineForOffset(index);
+ final float x = textView.getTotalPaddingLeft() - textView.getScrollX()
+ + layout.getPrimaryHorizontal(index);
+ final float y = textView.getTotalPaddingTop() - textView.getScrollY()
+ + layout.getLineTop(line);
+ return new float[]{x + xy[0], y + xy[1]};
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
new file mode 100644
index 0000000..37c7425
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static org.hamcrest.Matchers.is;
+
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewAssertion;
+import android.view.View;
+import android.widget.TextView;
+
+import junit.framework.AssertionFailedError;
+import org.hamcrest.Matcher;
+
+/**
+ * A collection of assertions on a {@link android.widget.TextView}.
+ */
+public final class TextViewAssertions {
+
+ private TextViewAssertions() {}
+
+ /**
+ * Returns a {@link ViewAssertion} that asserts that the text view has a specified
+ * selection.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a text view displayed on screen
+ * <ul>
+ *
+ * @param selection The expected selection.
+ */
+ public static ViewAssertion hasSelection(String selection) {
+ return hasSelection(is(selection));
+ }
+
+ /**
+ * Returns a {@link ViewAssertion} that asserts that the text view has a specified
+ * selection.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a text view displayed on screen
+ * <ul>
+ *
+ * @param selection A matcher representing the expected selection.
+ */
+ public static ViewAssertion hasSelection(Matcher<String> selection) {
+ return new TextSelectionAssertion(selection);
+ }
+
+ /**
+ * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at
+ * a specified index.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a text view displayed on screen
+ * <ul>
+ *
+ * @param index The expected index.
+ */
+ public static ViewAssertion hasInsertionPointerAtIndex(int index) {
+ return hasInsertionPointerAtIndex(is(index));
+ }
+
+ /**
+ * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at
+ * a specified index.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a text view displayed on screen
+ * <ul>
+ *
+ * @param index A matcher representing the expected index.
+ */
+ public static ViewAssertion hasInsertionPointerAtIndex(final Matcher<Integer> index) {
+ return new ViewAssertion() {
+ @Override
+ public void check(View view, NoMatchingViewException exception) {
+ if (view instanceof TextView) {
+ TextView textView = (TextView) view;
+ int selectionStart = textView.getSelectionStart();
+ int selectionEnd = textView.getSelectionEnd();
+ try {
+ assertThat(selectionStart, index);
+ assertThat(selectionEnd, index);
+ } catch (IndexOutOfBoundsException e) {
+ throw new AssertionFailedError(e.getMessage());
+ }
+ } else {
+ throw new AssertionFailedError("TextView not found");
+ }
+ }
+ };
+ }
+
+ /**
+ * A {@link ViewAssertion} to check the selected text in a {@link TextView}.
+ */
+ private static final class TextSelectionAssertion implements ViewAssertion {
+
+ private final Matcher<String> mSelection;
+
+ public TextSelectionAssertion(Matcher<String> selection) {
+ mSelection = checkNotNull(selection);
+ }
+
+ @Override
+ public void check(View view, NoMatchingViewException exception) {
+ if (view instanceof TextView) {
+ TextView textView = (TextView) view;
+ int selectionStart = textView.getSelectionStart();
+ int selectionEnd = textView.getSelectionEnd();
+ try {
+ String selectedText = textView.getText()
+ .subSequence(selectionStart, selectionEnd)
+ .toString();
+ assertThat(selectedText, mSelection);
+ } catch (IndexOutOfBoundsException e) {
+ throw new AssertionFailedError(e.getMessage());
+ }
+ } else {
+ throw new AssertionFailedError("TextView not found");
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index 5e7f127..c279c8f 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -1075,4 +1075,120 @@
assertTrue(subtypes2.isEmpty());
}
}
+
+ @SmallTest
+ public void testbuildInputMethodsAndSubtypesString() {
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ assertEquals("", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ map.put("ime0", new ArraySet<String>());
+ assertEquals("ime0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ map.put("ime0", subtypes1);
+ assertEquals("ime0;subtype0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ subtypes1.add("subtype1");
+ map.put("ime0", subtypes1);
+
+ // We do not expect what order will be used to concatenate items in
+ // InputMethodUtils.buildInputMethodsAndSubtypesString() hence enumerate all possible
+ // permutations here.
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0;subtype1");
+ validSequences.add("ime0;subtype1;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ map.put("ime0", new ArraySet<String>());
+ map.put("ime1", new ArraySet<String>());
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0:ime1");
+ validSequences.add("ime1:ime0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ map.put("ime0", subtypes1);
+ map.put("ime1", new ArraySet<String>());
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0:ime1");
+ validSequences.add("ime1;ime0;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ subtypes1.add("subtype1");
+ map.put("ime0", subtypes1);
+ map.put("ime1", new ArraySet<String>());
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0;subtype1:ime1");
+ validSequences.add("ime0;subtype1;subtype0:ime1");
+ validSequences.add("ime1:ime0;subtype0;subtype1");
+ validSequences.add("ime1:ime0;subtype1;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ map.put("ime0", subtypes1);
+
+ ArraySet<String> subtypes2 = new ArraySet<>();
+ subtypes2.add("subtype1");
+ map.put("ime1", subtypes2);
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0:ime1;subtype1");
+ validSequences.add("ime1;subtype1:ime0;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ subtypes1.add("subtype1");
+ map.put("ime0", subtypes1);
+
+ ArraySet<String> subtypes2 = new ArraySet<>();
+ subtypes2.add("subtype2");
+ subtypes2.add("subtype3");
+ map.put("ime1", subtypes2);
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0;subtype1:ime1;subtype2;subtype3");
+ validSequences.add("ime0;subtype1;subtype0:ime1;subtype2;subtype3");
+ validSequences.add("ime0;subtype0;subtype1:ime1;subtype3;subtype2");
+ validSequences.add("ime0;subtype1;subtype0:ime1;subtype3;subtype2");
+ validSequences.add("ime1;subtype2;subtype3:ime0;subtype0;subtype1");
+ validSequences.add("ime2;subtype3;subtype2:ime0;subtype0;subtype1");
+ validSequences.add("ime3;subtype2;subtype3:ime0;subtype1;subtype0");
+ validSequences.add("ime4;subtype3;subtype2:ime0;subtype1;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ }
}
diff --git a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
index 4c16154..67b12d7 100644
--- a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
@@ -30,7 +30,7 @@
static const char *classPathName = "com/framework/shareduid/bit32/Native";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"add", "(II)I", (void*)add },
};
@@ -38,7 +38,7 @@
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
- JNINativeMethod* gMethods, int numMethods)
+ const JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
index c2f9f529..342b3bc 100644
--- a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
@@ -30,7 +30,7 @@
static const char *classPathName = "com/framework/shareduid/bit64/Native";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"add", "(II)I", (void*)add },
};
@@ -38,7 +38,7 @@
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
- JNINativeMethod* gMethods, int numMethods)
+ const JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
index 5d3ca06..9b38e3e 100644
--- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
@@ -30,7 +30,7 @@
static const char *classPathName = "com/framework/shareduid/dual/Native";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"add", "(II)I", (void*)add },
};
@@ -38,7 +38,7 @@
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
- JNINativeMethod* gMethods, int numMethods)
+ const JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
diff --git a/docs/html/guide/topics/renderscript/reference/rs_for_each.jd b/docs/html/guide/topics/renderscript/reference/rs_for_each.jd
index abbbda7..9ba5614 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_for_each.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_for_each.jd
@@ -205,7 +205,7 @@
<span class='normal'>: Handle to a kernel invocation context</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: const struct rs_kernel_context_t * Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: const struct rs_kernel_context_t * Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> The kernel context contains common characteristics of the allocations being iterated
over, like dimensions. It also contains rarely used indices of the currently processed
diff --git a/docs/html/guide/topics/renderscript/reference/rs_graphics.jd b/docs/html/guide/topics/renderscript/reference/rs_graphics.jd
index 1b115fe..b04451c 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_graphics.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_graphics.jd
@@ -502,7 +502,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_BLEND_DST_ZERO = 0</th><td></td></tr>
@@ -527,7 +527,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_BLEND_SRC_ZERO = 0</th><td></td></tr>
@@ -553,7 +553,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_CULL_BACK = 0</th><td></td></tr>
@@ -573,7 +573,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_DEPTH_FUNC_ALWAYS = 0</th><td>Always drawn</td></tr>
@@ -599,11 +599,7 @@
<span class='normal'>: Handle to a Font</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-)) When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript font object.
@@ -619,11 +615,7 @@
<span class='normal'>: Handle to a Mesh</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-)) When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript mesh object.
@@ -640,7 +632,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_PRIMITIVE_POINT = 0</th><td>Vertex data will be rendered as a series of points</td></tr>
@@ -664,11 +656,7 @@
<span class='normal'>: Handle to a ProgramFragment</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-)) When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript ProgramFragment object.
@@ -684,11 +672,7 @@
<span class='normal'>: Handle to a ProgramRaster</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-)) When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript ProgramRaster object.
@@ -704,11 +688,7 @@
<span class='normal'>: Handle to a ProgramStore</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-)) When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript ProgramStore object.
@@ -724,11 +704,7 @@
<span class='normal'>: Handle to a ProgramVertex</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-)) When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript ProgramVertex object.
diff --git a/docs/html/guide/topics/renderscript/reference/rs_math.jd b/docs/html/guide/topics/renderscript/reference/rs_math.jd
index 13513e9..e1e7805 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_math.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_math.jd
@@ -3968,16 +3968,31 @@
<td> </td>
</tr>
<tr>
+ <td><a href='rs_value_types.html#android_rs:float2'>float2</a> max(<a href='rs_value_types.html#android_rs:float2'>float2</a> a, float b);
+</td>
+ <td> </td>
+ </tr>
+ <tr>
<td><a href='rs_value_types.html#android_rs:float2'>float2</a> max(<a href='rs_value_types.html#android_rs:float2'>float2</a> a, <a href='rs_value_types.html#android_rs:float2'>float2</a> b);
</td>
<td> </td>
</tr>
<tr>
+ <td><a href='rs_value_types.html#android_rs:float3'>float3</a> max(<a href='rs_value_types.html#android_rs:float3'>float3</a> a, float b);
+</td>
+ <td> </td>
+ </tr>
+ <tr>
<td><a href='rs_value_types.html#android_rs:float3'>float3</a> max(<a href='rs_value_types.html#android_rs:float3'>float3</a> a, <a href='rs_value_types.html#android_rs:float3'>float3</a> b);
</td>
<td> </td>
</tr>
<tr>
+ <td><a href='rs_value_types.html#android_rs:float4'>float4</a> max(<a href='rs_value_types.html#android_rs:float4'>float4</a> a, float b);
+</td>
+ <td> </td>
+ </tr>
+ <tr>
<td><a href='rs_value_types.html#android_rs:float4'>float4</a> max(<a href='rs_value_types.html#android_rs:float4'>float4</a> a, <a href='rs_value_types.html#android_rs:float4'>float4</a> b);
</td>
<td> </td>
@@ -4172,16 +4187,31 @@
<td> </td>
</tr>
<tr>
+ <td><a href='rs_value_types.html#android_rs:float2'>float2</a> min(<a href='rs_value_types.html#android_rs:float2'>float2</a> a, float b);
+</td>
+ <td> </td>
+ </tr>
+ <tr>
<td><a href='rs_value_types.html#android_rs:float2'>float2</a> min(<a href='rs_value_types.html#android_rs:float2'>float2</a> a, <a href='rs_value_types.html#android_rs:float2'>float2</a> b);
</td>
<td> </td>
</tr>
<tr>
+ <td><a href='rs_value_types.html#android_rs:float3'>float3</a> min(<a href='rs_value_types.html#android_rs:float3'>float3</a> a, float b);
+</td>
+ <td> </td>
+ </tr>
+ <tr>
<td><a href='rs_value_types.html#android_rs:float3'>float3</a> min(<a href='rs_value_types.html#android_rs:float3'>float3</a> a, <a href='rs_value_types.html#android_rs:float3'>float3</a> b);
</td>
<td> </td>
</tr>
<tr>
+ <td><a href='rs_value_types.html#android_rs:float4'>float4</a> min(<a href='rs_value_types.html#android_rs:float4'>float4</a> a, float b);
+</td>
+ <td> </td>
+ </tr>
+ <tr>
<td><a href='rs_value_types.html#android_rs:float4'>float4</a> min(<a href='rs_value_types.html#android_rs:float4'>float4</a> a, <a href='rs_value_types.html#android_rs:float4'>float4</a> b);
</td>
<td> </td>
diff --git a/docs/html/guide/topics/renderscript/reference/rs_object_types.jd b/docs/html/guide/topics/renderscript/reference/rs_object_types.jd
index f342896..ac47454 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_object_types.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_object_types.jd
@@ -99,7 +99,7 @@
<span class='normal'>: Handle to an allocation</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE </p>
+<p></p>
<p> An opaque handle to a RenderScript allocation.
</p>
@@ -116,7 +116,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X = 0</th><td></td></tr>
@@ -139,7 +139,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_ALLOCATION_USAGE_SCRIPT = 0x0001</th><td>Allocation is bound to and accessed by scripts.</td></tr>
@@ -165,7 +165,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_KIND_USER = 0</th><td>No special interpretation.</td></tr>
@@ -202,7 +202,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_TYPE_NONE = 0</th><td>Element is a complex type, i.e. a struct.</td></tr>
@@ -253,7 +253,7 @@
<span class='normal'>: Handle to an element</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE </p>
+<p></p>
<p> An opaque handle to a RenderScript element.
</p>
@@ -269,7 +269,7 @@
<span class='normal'>: Handle to a Sampler</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE </p>
+<p></p>
<p> An opaque handle to a RenderScript sampler object.
</p>
@@ -286,7 +286,7 @@
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_SAMPLER_NEAREST = 0</th><td></td></tr>
@@ -308,7 +308,7 @@
<span class='normal'>: Handle to a Script</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE </p>
+<p></p>
<p> An opaque handle to a RenderScript script object.
</p>
@@ -324,7 +324,7 @@
<span class='normal'>: Handle to a Type</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE </p>
+<p></p>
<p> An opaque handle to a RenderScript type.
</p>
diff --git a/docs/html/guide/topics/renderscript/reference/rs_time.jd b/docs/html/guide/topics/renderscript/reference/rs_time.jd
index 27044a3..a1cedfb 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_time.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_time.jd
@@ -78,9 +78,9 @@
<span class='normal'>: Seconds since January 1, 1970</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: int When compiling for 32 bits.
+<p>A typedef of: int When compiling for 32 bits.
</p>
-<p>A typedef of: long When compiling for 64 bits.
+<p>A typedef of: long When compiling for 64 bits.
</p>
<p> Calendar time interpreted as seconds elapsed since the Epoch (00:00:00 on
January 1, 1970, Coordinated Universal Time (UTC)).
diff --git a/docs/html/guide/topics/renderscript/reference/rs_value_types.jd b/docs/html/guide/topics/renderscript/reference/rs_value_types.jd
index 85c7a5c..2bd49dc 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_value_types.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_value_types.jd
@@ -25,7 +25,7 @@
</p>
<p> To create vector literals, use the vector type followed by the values enclosed
-between parentheses, e.g. <code>(float3)(1.0f, 2.0f, 3.0f)</code>.
+between curly braces, e.g. <code>(float3){1.0f, 2.0f, 3.0f}</code>.
</p>
<p> Entries of a vector can be accessed using different naming styles.
@@ -644,7 +644,7 @@
<span class='normal'>: 16 bit floating point value</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: __fp16 Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: __fp16 Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> A 16 bit floating point value.
</p>
@@ -658,7 +658,7 @@
<span class='normal'>: Two 16 bit floats</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: half __attribute__((ext_vector_type(2))) Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: half __attribute__((ext_vector_type(2))) Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> Vector version of the half float type. Provides two half fields packed
into a single 32 bit field with 32 bit alignment.
@@ -673,7 +673,7 @@
<span class='normal'>: Three 16 bit floats</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: half __attribute__((ext_vector_type(3))) Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: half __attribute__((ext_vector_type(3))) Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> Vector version of the half float type. Provides three half fields packed
into a single 64 bit field with 64 bit alignment.
@@ -688,7 +688,7 @@
<span class='normal'>: Four 16 bit floats</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: half __attribute__((ext_vector_type(4))) Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: half __attribute__((ext_vector_type(4))) Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> Vector version of the half float type. Provides four half fields packed
into a single 64 bit field with 64 bit alignment.
@@ -771,9 +771,9 @@
<span class='normal'>: 64 bit signed integer</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: long long Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21 and higher</a>
+<p>A typedef of: long long Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21 and higher</a>
</p>
-<p>A typedef of: long Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21</a>
+<p>A typedef of: long Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21</a>
</p>
<p> A 64 bit signed integer type.
</p>
@@ -960,9 +960,9 @@
<span class='normal'>: Unsigned size type</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: uint64_t When compiling for 64 bits.
+<p>A typedef of: uint64_t When compiling for 64 bits.
</p>
-<p>A typedef of: uint32_t When compiling for 32 bits.
+<p>A typedef of: uint32_t When compiling for 32 bits.
</p>
<p> Unsigned size type. The number of bits depend on the compilation flags.
</p>
@@ -976,9 +976,9 @@
<span class='normal'>: Signed size type</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: int64_t When compiling for 64 bits.
+<p>A typedef of: int64_t When compiling for 64 bits.
</p>
-<p>A typedef of: int32_t When compiling for 32 bits.
+<p>A typedef of: int32_t When compiling for 32 bits.
</p>
<p> Signed size type. The number of bits depend on the compilation flags.
</p>
@@ -1128,9 +1128,9 @@
<span class='normal'>: 64 bit unsigned integer</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: unsigned long long Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21 and higher</a>
+<p>A typedef of: unsigned long long Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21 and higher</a>
</p>
-<p>A typedef of: unsigned long Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21</a>
+<p>A typedef of: unsigned long Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21</a>
</p>
<p> A 64 bit unsigned integer type.
</p>
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index 9dc0ed1..22ad0c9 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -665,7 +665,7 @@
<li>Added support for a Collapse icon description in the {@link android.support.v7.widget.Toolbar}
class.</li>
<li>Updated the {@link android.support.v7.widget.SearchView} widget to support displaying
- the {@link android.support.v7.mediarouter.R.attr#commitIcon}. </li>
+ the {@link android.support.v7.appcompat.R.attr#commitIcon}. </li>
<li>Removed the <code>buttonGravity</code> attribute from the
{@link android.support.v7.widget.Toolbar} class. </li>
</ul>
diff --git a/docs/html/training/location/geofencing.jd b/docs/html/training/location/geofencing.jd
index 59fc4c6..556329c 100644
--- a/docs/html/training/location/geofencing.jd
+++ b/docs/html/training/location/geofencing.jd
@@ -100,8 +100,8 @@
<h3>Create geofence objects</h3>
<p>
- First, use <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.
- html">Geofence.Builder</a></code> to create a geofence, setting the desired radius, duration, and
+ First, use <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.html">
+ Geofence.Builder</a></code> to create a geofence, setting the desired radius, duration, and
transition types for the geofence. For example, to populate a list object named
{@code mGeofenceList}:
</p>
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index 52597e1..63fe8ac 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -702,7 +702,7 @@
return status;
}
-static JNINativeMethod nativeMethods[] = {
+static const JNINativeMethod nativeMethods[] = {
{"_initialize", "()I",
(void*)android_drm_DrmManagerClient_initialize},
diff --git a/graphics/java/android/graphics/Interpolator.java b/graphics/java/android/graphics/Interpolator.java
index f695a9e..1045464 100644
--- a/graphics/java/android/graphics/Interpolator.java
+++ b/graphics/java/android/graphics/Interpolator.java
@@ -147,11 +147,12 @@
@Override
protected void finalize() throws Throwable {
nativeDestructor(native_instance);
+ native_instance = 0; // Other finalizers can still call us.
}
private int mValueCount;
private int mFrameCount;
- private final long native_instance;
+ private long native_instance;
private static native long nativeConstructor(int valueCount, int frameCount);
private static native void nativeDestructor(long native_instance);
diff --git a/graphics/java/android/graphics/MaskFilter.java b/graphics/java/android/graphics/MaskFilter.java
index 27a7dda..d474315 100644
--- a/graphics/java/android/graphics/MaskFilter.java
+++ b/graphics/java/android/graphics/MaskFilter.java
@@ -25,6 +25,7 @@
protected void finalize() throws Throwable {
nativeDestructor(native_instance);
+ native_instance = 0; // Other finalizers can still call us.
}
private static native void nativeDestructor(long native_filter);
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index 90e5a4e..1e8f11b 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -827,6 +827,7 @@
protected void finalize() throws Throwable {
try {
finalizer(native_instance);
+ native_instance = 0; // Other finalizers can still call us.
} finally {
super.finalize();
}
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index b9a1eda..5efc00c 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -71,7 +71,7 @@
*
* @hide
*/
- public final long mNativeChunk;
+ public long mNativeChunk;
private Paint mPaint;
private String mSrcName;
@@ -121,6 +121,7 @@
if (mNativeChunk != 0) {
// only attempt to destroy correctly initilized chunks
nativeFinalize(mNativeChunk);
+ mNativeChunk = 0;
}
} finally {
super.finalize();
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index ce35b87..6582b7e 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2471,6 +2471,7 @@
protected void finalize() throws Throwable {
try {
finalizer(mNativePaint);
+ mNativePaint = 0;
} finally {
super.finalize();
}
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 91e704a..da3deff 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -27,7 +27,7 @@
/**
* @hide
*/
- public final long mNativePath;
+ public long mNativePath;
/**
* @hide
@@ -746,6 +746,7 @@
protected void finalize() throws Throwable {
try {
finalizer(mNativePath);
+ mNativePath = 0; // Other finalizers can still call us.
} finally {
super.finalize();
}
diff --git a/graphics/java/android/graphics/PathEffect.java b/graphics/java/android/graphics/PathEffect.java
index 617dfca..3292501 100644
--- a/graphics/java/android/graphics/PathEffect.java
+++ b/graphics/java/android/graphics/PathEffect.java
@@ -25,6 +25,7 @@
protected void finalize() throws Throwable {
nativeDestructor(native_instance);
+ native_instance = 0; // Other finalizers can still call us.
}
private static native void nativeDestructor(long native_patheffect);
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 7cc9765..0416159 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -142,6 +142,7 @@
protected void finalize() throws Throwable {
native_destroy(native_instance);
+ native_instance = 0; // Other finalizers can still call us.
}
private static native long native_create(long native_path, boolean forceClosed);
@@ -154,6 +155,6 @@
private static native boolean native_nextContour(long native_instance);
private static native void native_destroy(long native_instance);
- /* package */private final long native_instance;
+ /* package */private long native_instance;
}
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 0e55089..28d8690 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -29,7 +29,7 @@
*/
public class Picture {
private Canvas mRecordingCanvas;
- private final long mNativePicture;
+ private long mNativePicture;
private static final int WORKING_STREAM_STORAGE = 16 * 1024;
@@ -60,6 +60,7 @@
protected void finalize() throws Throwable {
try {
nativeDestructor(mNativePicture);
+ mNativePicture = 0;
} finally {
super.finalize();
}
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index 727723d..de89ad0 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -30,7 +30,7 @@
/**
* @hide
*/
- public final long mNativeRegion;
+ public long mNativeRegion;
// the native values for these must match up with the enum in SkRegion.h
public enum Op {
@@ -380,6 +380,7 @@
protected void finalize() throws Throwable {
try {
nativeDestructor(mNativeRegion);
+ mNativeRegion = 0;
} finally {
super.finalize();
}
diff --git a/graphics/java/android/graphics/RegionIterator.java b/graphics/java/android/graphics/RegionIterator.java
index 8401adb..443b23c 100644
--- a/graphics/java/android/graphics/RegionIterator.java
+++ b/graphics/java/android/graphics/RegionIterator.java
@@ -43,12 +43,13 @@
protected void finalize() throws Throwable {
nativeDestructor(mNativeIter);
+ mNativeIter = 0; // Other finalizers can still call us.
}
private static native long nativeConstructor(long native_region);
private static native void nativeDestructor(long native_iter);
private static native boolean nativeNext(long native_iter, Rect r);
- private final long mNativeIter;
+ private long mNativeIter;
}
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index a96d2cb..adb282f 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -91,6 +91,7 @@
super.finalize();
} finally {
nativeDestructor(native_instance);
+ native_instance = 0; // Other finalizers can still call us.
}
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index db42314..7eb5584 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -358,6 +358,7 @@
protected void finalize() throws Throwable {
try {
nativeUnref(native_instance);
+ native_instance = 0; // Other finalizers can still call us.
} finally {
super.finalize();
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index abb51db..1857345 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -217,7 +217,7 @@
}
@Override
- protected boolean onLevelChange(float level) {
+ protected boolean onLevelChange(int level) {
return mAnimatedVectorState.mVectorDrawable.setLevel(level);
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 3b92507..31fccd0 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -52,6 +52,8 @@
public static final int HORIZONTAL = 1;
public static final int VERTICAL = 2;
+ private static final int MAX_LEVEL = 10000;
+
private final Rect mTmpRect = new Rect();
private ClipState mState;
@@ -141,7 +143,7 @@
}
@Override
- protected boolean onLevelChange(float level) {
+ protected boolean onLevelChange(int level) {
super.onLevelChange(level);
invalidateSelf();
return true;
@@ -151,12 +153,12 @@
public int getOpacity() {
final Drawable dr = getDrawable();
final int opacity = dr.getOpacity();
- if (opacity == PixelFormat.TRANSPARENT || dr.getLevelFloat() == 0) {
+ if (opacity == PixelFormat.TRANSPARENT || dr.getLevel() == 0) {
return PixelFormat.TRANSPARENT;
}
- final float level = getLevelFloat();
- if (level >= MAX_LEVEL_FLOAT) {
+ final int level = getLevel();
+ if (level >= MAX_LEVEL) {
return dr.getOpacity();
}
@@ -167,24 +169,24 @@
@Override
public void draw(Canvas canvas) {
final Drawable dr = getDrawable();
- if (dr.getLevelFloat() == 0) {
+ if (dr.getLevel() == 0) {
return;
}
final Rect r = mTmpRect;
final Rect bounds = getBounds();
- final float level = getLevelFloat();
+ final int level = getLevel();
int w = bounds.width();
- final int iw = 0;
+ final int iw = 0; //mState.mDrawable.getIntrinsicWidth();
if ((mState.mOrientation & HORIZONTAL) != 0) {
- w -= Math.round((w - iw) * (MAX_LEVEL_FLOAT - level) / MAX_LEVEL_FLOAT);
+ w -= (w - iw) * (MAX_LEVEL - level) / MAX_LEVEL;
}
int h = bounds.height();
- final int ih = 0;
+ final int ih = 0; //mState.mDrawable.getIntrinsicHeight();
if ((mState.mOrientation & VERTICAL) != 0) {
- h -= Math.round((h - ih) * (MAX_LEVEL_FLOAT - level) / MAX_LEVEL_FLOAT);
+ h -= (h - ih) * (MAX_LEVEL - level) / MAX_LEVEL;
}
final int layoutDirection = getLayoutDirection();
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index fb771550..b95c183 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -41,7 +41,6 @@
import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
-import android.util.FloatProperty;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;
@@ -127,19 +126,12 @@
* document.</p></div>
*/
public abstract class Drawable {
-
private static final Rect ZERO_BOUNDS_RECT = new Rect();
static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
- /** The standard maximum value for calls to {@link #setLevel(int)}. */
- public static final int MAX_LEVEL = 10000;
-
- /** The standard maximum value for calls to {@link #setLevel(float)}. */
- public static final float MAX_LEVEL_FLOAT = 10000.0f;
-
private int[] mStateSet = StateSet.WILD_CARD;
- private float mLevel = 0.0f;
+ private int mLevel = 0;
private int mChangingConfigurations = 0;
private Rect mBounds = ZERO_BOUNDS_RECT; // lazily becomes a new Rect()
private WeakReference<Callback> mCallback = null;
@@ -719,63 +711,22 @@
}
/**
- * Sets the level for the drawable as an integer value where typically the
- * minimum level is 0 and the maximum is 10000 {@link #MAX_LEVEL}; however,
- * the range may vary based on the Drawable implementation and is not
- * clamped.
- * <p>
- * This allows a drawable to vary its imagery based on a continuous
- * controller. For example, it may be used to show progress or volume
- * level.
- * <p>
- * Use #setLevelFloat(float) to set the level as a high-precision
- * floating-point value.
+ * Specify the level for the drawable. This allows a drawable to vary its
+ * imagery based on a continuous controller, for example to show progress
+ * or volume level.
*
- * @param level the new level, typically between 0 and 10000
- * @return {@code true} if this change in level has caused the appearance
- * of the drawable to change and will require a subsequent call to
- * invalidate, {@code false} otherwise
- * @see #getLevel()
- * @see #setLevel(float)
- * @see #onLevelChange(int)
+ * <p>If the new level you are supplying causes the appearance of the
+ * Drawable to change, then it is responsible for calling
+ * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em>
+ * true will be returned from this function.
+ *
+ * @param level The new level, from 0 (minimum) to 10000 (maximum).
+ *
+ * @return Returns true if this change in level has caused the appearance
+ * of the Drawable to change (hence requiring an invalidate), otherwise
+ * returns false.
*/
public final boolean setLevel(int level) {
- return setLevel((float) level);
- }
-
- /**
- * Returns the current level as a rounded integer value.
- * <p>
- * Use #getLevelFloat() to return the level as a high-precision
- * floating-point value.
- *
- * @return the current level, typically between 0 and 10000
- * @see #setLevel(int)
- * @see #getLevelFloat()
- */
- public final int getLevel() {
- return Math.round(mLevel);
- }
-
- /**
- * Sets the level for the drawable as a floating-point value where
- * typically the minimum level is 0.0 and the maximum is 10000.0
- * {@link #MAX_LEVEL_FLOAT}; however, the range may vary based on the
- * Drawable implementation and is not clamped.
- * <p>
- * This allows a drawable to vary its imagery based on a continuous
- * controller. For example, it may be used to show progress or volume
- * level.
- *
- * @param level the new level, typically between 0.0 and 10000.0
- * ({@link #MAX_LEVEL_FLOAT})
- * @return {@code true} if this change in level has caused the appearance
- * of the drawable to change and will require a subsequent call to
- * invalidate, {@code false} otherwise
- * @see #getLevelFloat()
- * @see #onLevelChange(float)
- */
- public final boolean setLevel(float level) {
if (mLevel != level) {
mLevel = level;
return onLevelChange(level);
@@ -784,13 +735,11 @@
}
/**
- * Returns the current level as a floating-point value.
+ * Retrieve the current level.
*
- * @return the current level, typically between 0.0 and 10000.0
- * ({@link #MAX_LEVEL_FLOAT})
- * @see #setLevel(float)
+ * @return int Current level, from 0 (minimum) to 10000 (maximum).
*/
- public final float getLevelFloat() {
+ public final int getLevel() {
return mLevel;
}
@@ -945,47 +894,14 @@
* last state.
*/
protected boolean onStateChange(int[] state) { return false; }
-
- /**
- * Called when the drawable level changes.
- * <p>
- * Override this in your subclass to change appearance if you vary based on
- * level and do not need floating-point accuracy. To handle changes with
- * higher accuracy, override {@link #onLevelChange(float)} instead.
- * <p>
- * <strong>Note:</strong> Do not override both this method and
- * {@link #onLevelChange(float)}. Only override one method.
- *
- * @param level the level as an integer value, typically between 0
- * (minimum) and 10000 ({@link #MAX_LEVEL})
- * @return {@code true} if the level change has caused the appearance of
- * the drawable to change such that it needs to be redrawn, or
- * {@code false} if there is no need to redraw
+ /** Override this in your subclass to change appearance if you vary based
+ * on level.
+ * @return Returns true if the level change has caused the appearance of
+ * the Drawable to change (that is, it needs to be drawn), else false
+ * if it looks the same and there is no need to redraw it since its
+ * last level.
*/
protected boolean onLevelChange(int level) { return false; }
-
- /**
- * Called when the drawable level changes.
- * <p>
- * Override this in your subclass to change appearance if you vary based on
- * level and need floating-point accuracy.
- * <p>
- * <strong>Note:</strong> Do not override both this method and
- * {@link #onLevelChange(int)}. Only override one method. If your app
- * targets SDK <= 23 ({@link android.os.Build.VERSION_CODES#M M}), you
- * will need to override {@link #onLevelChange(int)} to receive callbacks
- * on devices running Android M and below.
- *
- * @param level the level as a floating-point value, typically between 0.0
- * and 10000.0 ({@link #MAX_LEVEL_FLOAT})
- * @return {@code true} if the level change has caused the appearance of
- * the drawable to change such that it needs to be redrawn, or
- * {@code false} if there is no need to redraw
- */
- protected boolean onLevelChange(float level) {
- return onLevelChange(Math.round(level));
- }
-
/**
* Override this in your subclass to change appearance if you vary based on
* the bounds.
@@ -1412,23 +1328,6 @@
}
/**
- * Animatable property for Drawable level.
- *
- * @hide Until Drawable animations have been cleaned up.
- */
- public static final FloatProperty<Drawable> LEVEL = new FloatProperty<Drawable>("levelFloat") {
- @Override
- public Float get(Drawable object) {
- return object.getLevelFloat();
- }
-
- @Override
- public void setValue(Drawable object, float value) {
- object.setLevel(value);
- }
- };
-
- /**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
*/
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 0210ddb..1f7d996 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -322,7 +322,7 @@
}
@Override
- protected boolean onLevelChange(float level) {
+ protected boolean onLevelChange(int level) {
if (mLastDrawable != null) {
return mLastDrawable.setLevel(level);
}
@@ -420,14 +420,23 @@
return mCurIndex;
}
- public boolean selectDrawable(int idx) {
- if (idx == mCurIndex) {
+ /**
+ * Sets the currently displayed drawable by index.
+ * <p>
+ * If an invalid index is specified, the current drawable will be set to
+ * {@code null} and the index will be set to {@code -1}.
+ *
+ * @param index the index of the drawable to display
+ * @return {@code true} if the drawable changed, {@code false} otherwise
+ */
+ public boolean selectDrawable(int index) {
+ if (index == mCurIndex) {
return false;
}
final long now = SystemClock.uptimeMillis();
- if (DEBUG) android.util.Log.i(TAG, toString() + " from " + mCurIndex + " to " + idx
+ if (DEBUG) android.util.Log.i(TAG, toString() + " from " + mCurIndex + " to " + index
+ ": exit=" + mDrawableContainerState.mExitFadeDuration
+ " enter=" + mDrawableContainerState.mEnterFadeDuration);
@@ -448,10 +457,10 @@
mCurrDrawable.setVisible(false, false);
}
- if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
- final Drawable d = mDrawableContainerState.getChild(idx);
+ if (index >= 0 && index < mDrawableContainerState.mNumChildren) {
+ final Drawable d = mDrawableContainerState.getChild(index);
mCurrDrawable = d;
- mCurIndex = idx;
+ mCurIndex = index;
if (d != null) {
if (mDrawableContainerState.mEnterFadeDuration > 0) {
mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
@@ -510,7 +519,7 @@
d.setVisible(isVisible(), true);
d.setDither(mDrawableContainerState.mDither);
d.setState(getState());
- d.setLevel(getLevelFloat());
+ d.setLevel(getLevel());
d.setBounds(getBounds());
d.setLayoutDirection(getLayoutDirection());
d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index 57b4db2..9185e1a 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -92,7 +92,7 @@
// Only call setters for data that's stored in the base Drawable.
dr.setVisible(isVisible(), true);
dr.setState(getState());
- dr.setLevel(getLevelFloat());
+ dr.setLevel(getLevel());
dr.setBounds(getBounds());
dr.setLayoutDirection(getLayoutDirection());
@@ -286,7 +286,7 @@
}
@Override
- protected boolean onLevelChange(float level) {
+ protected boolean onLevelChange(int level) {
return mDrawable != null && mDrawable.setLevel(level);
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 15295a0..d7fd8a5 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -530,8 +530,8 @@
* {@code false} otherwise
*
* @see #mutate()
- * @see #setLevel(float)
- * @see #getLevelFloat()
+ * @see #setLevel(int)
+ * @see #getLevel()
* @see #isUseLevel()
*/
public void setUseLevel(boolean useLevel) {
@@ -764,7 +764,7 @@
if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) return mRingPath;
mPathIsDirty = false;
- float sweep = st.mUseLevelForShape ? (360.0f * getLevelFloat() / MAX_LEVEL_FLOAT) : 360f;
+ float sweep = st.mUseLevelForShape ? (360.0f * getLevel() / 10000.0f) : 360f;
RectF bounds = new RectF(mRect);
@@ -990,7 +990,7 @@
}
@Override
- protected boolean onLevelChange(float level) {
+ protected boolean onLevelChange(int level) {
super.onLevelChange(level);
mGradientIsDirty = true;
mPathIsDirty = true;
@@ -1026,7 +1026,7 @@
final float x0, x1, y0, y1;
if (st.mGradient == LINEAR_GRADIENT) {
- final float level = st.mUseLevel ? getLevelFloat() / MAX_LEVEL_FLOAT : 1.0f;
+ final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
switch (st.mOrientation) {
case TOP_BOTTOM:
x0 = r.left; y0 = r.top;
@@ -1080,7 +1080,7 @@
}
if (st.mUseLevel) {
- radius *= getLevelFloat() / MAX_LEVEL_FLOAT;
+ radius *= getLevel() / 10000.0f;
}
mGradientRadius = radius;
@@ -1115,7 +1115,7 @@
tempPositions = st.mTempPositions = new float[length + 1];
}
- final float level = getLevelFloat() / MAX_LEVEL_FLOAT;
+ final float level = getLevel() / 10000.0f;
for (int i = 0; i < length; i++) {
tempPositions[i] = i * fraction * level;
}
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index c9e38b9..1a0ba6f 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -1400,7 +1400,7 @@
}
@Override
- protected boolean onLevelChange(float level) {
+ protected boolean onLevelChange(int level) {
boolean changed = false;
final ChildDrawable[] array = mLayerState.mChildren;
@@ -1733,7 +1733,7 @@
clone.setCallback(owner);
clone.setLayoutDirection(dr.getLayoutDirection());
clone.setBounds(dr.getBounds());
- clone.setLevel(dr.getLevelFloat());
+ clone.setLevel(dr.getLevel());
} else {
clone = null;
}
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index 09d8b6f..b01c643 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -69,16 +69,15 @@
if (drawable != null) {
mLevelListState.addLevel(low, high, drawable);
// in case the new state matches our current state...
- onLevelChange(getLevelFloat());
+ onLevelChange(getLevel());
}
}
// overrides from Drawable
@Override
- protected boolean onLevelChange(float level) {
- final int nearestLevel = Math.round(level);
- final int idx = mLevelListState.indexOfLevel(nearestLevel);
+ protected boolean onLevelChange(int level) {
+ int idx = mLevelListState.indexOfLevel(level);
if (selectDrawable(idx)) {
return true;
}
@@ -142,7 +141,7 @@
mLevelListState.addLevel(low, high, dr);
}
- onLevelChange(getLevelFloat());
+ onLevelChange(getLevel());
}
@Override
@@ -241,7 +240,7 @@
private LevelListDrawable(LevelListState state, Resources res) {
final LevelListState as = new LevelListState(state, this, res);
setConstantState(as);
- onLevelChange(getLevelFloat());
+ onLevelChange(getLevel());
}
}
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 71c9977..036a078 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -303,10 +303,10 @@
}
@Override
- protected boolean onLevelChange(float level) {
+ protected boolean onLevelChange(int level) {
super.onLevelChange(level);
- final float value = level / (float) MAX_LEVEL_FLOAT;
+ final float value = level / (float) MAX_LEVEL;
final float degrees = MathUtils.lerp(mState.mFromDegrees, mState.mToDegrees, value);
mState.mCurrentDegrees = degrees;
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 38c6b80..f9206b7 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -35,21 +35,33 @@
/**
* A Drawable that changes the size of another Drawable based on its current
- * level value. You can control how much the child Drawable changes in width
+ * level value. You can control how much the child Drawable changes in width
* and height based on the level, as well as a gravity to control where it is
- * placed in its overall container. Most often used to implement things like
+ * placed in its overall container. Most often used to implement things like
* progress bars.
- *
- * <p>It can be defined in an XML file with the <code><scale></code> element. For more
- * information, see the guide to <a
- * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ * <p>
+ * The default level may be specified from XML using the
+ * {@link android.R.styleable#ScaleDrawable_level android:level} property. When
+ * this property is not specified, the default level is 0, which corresponds to
+ * zero height and/or width depending on the values specified for
+ * {@code android.R.styleable#ScaleDrawable_scaleWidth scaleWidth} and
+ * {@code android.R.styleable#ScaleDrawable_scaleHeight scaleHeight}. At run
+ * time, the level may be set via {@link #setLevel(int)}.
+ * <p>
+ * A scale drawable may be defined in an XML file with the {@code <scale>}
+ * element. For more information, see the guide to
+ * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable
+ * Resources</a>.
*
* @attr ref android.R.styleable#ScaleDrawable_scaleWidth
* @attr ref android.R.styleable#ScaleDrawable_scaleHeight
* @attr ref android.R.styleable#ScaleDrawable_scaleGravity
* @attr ref android.R.styleable#ScaleDrawable_drawable
+ * @attr ref android.R.styleable#ScaleDrawable_level
*/
public class ScaleDrawable extends DrawableWrapper {
+ private static final int MAX_LEVEL = 10000;
+
private final Rect mTmpRect = new Rect();
private ScaleState mState;
@@ -90,6 +102,8 @@
inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
+
+ updateLocalState();
}
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
@@ -115,6 +129,8 @@
R.styleable.ScaleDrawable_scaleGravity, state.mGravity);
state.mUseIntrinsicSizeAsMin = a.getBoolean(
R.styleable.ScaleDrawable_useIntrinsicSizeAsMinimum, state.mUseIntrinsicSizeAsMin);
+ state.mInitialLevel = a.getInt(
+ R.styleable.ScaleDrawable_level, state.mInitialLevel);
final Drawable dr = a.getDrawable(R.styleable.ScaleDrawable_drawable);
if (dr != null) {
@@ -163,12 +179,14 @@
// The drawable may have changed as a result of applying the theme, so
// apply the theme to the wrapped drawable last.
super.applyTheme(t);
+
+ updateLocalState();
}
@Override
public void draw(Canvas canvas) {
final Drawable d = getDrawable();
- if (d != null && d.getLevelFloat() != 0) {
+ if (d != null && d.getLevel() != 0) {
d.draw(canvas);
}
}
@@ -176,12 +194,12 @@
@Override
public int getOpacity() {
final Drawable d = getDrawable();
- if (d.getLevelFloat() == 0) {
+ if (d.getLevel() == 0) {
return PixelFormat.TRANSPARENT;
}
final int opacity = d.getOpacity();
- if (opacity == PixelFormat.OPAQUE && d.getLevelFloat() < MAX_LEVEL_FLOAT) {
+ if (opacity == PixelFormat.OPAQUE && d.getLevel() < MAX_LEVEL) {
return PixelFormat.TRANSLUCENT;
}
@@ -189,7 +207,7 @@
}
@Override
- protected boolean onLevelChange(float level) {
+ protected boolean onLevelChange(int level) {
super.onLevelChange(level);
onBoundsChange(getBounds());
invalidateSelf();
@@ -201,20 +219,18 @@
final Drawable d = getDrawable();
final Rect r = mTmpRect;
final boolean min = mState.mUseIntrinsicSizeAsMin;
- final float level = getLevelFloat();
+ final int level = getLevel();
int w = bounds.width();
if (mState.mScaleWidth > 0) {
final int iw = min ? d.getIntrinsicWidth() : 0;
- w -= (int) ((w - iw) * (MAX_LEVEL_FLOAT - level)
- * mState.mScaleWidth / MAX_LEVEL_FLOAT);
+ w -= (int) ((w - iw) * (MAX_LEVEL - level) * mState.mScaleWidth / MAX_LEVEL);
}
int h = bounds.height();
if (mState.mScaleHeight > 0) {
final int ih = min ? d.getIntrinsicHeight() : 0;
- h -= (int) ((h - ih) * (MAX_LEVEL_FLOAT - level)
- * mState.mScaleHeight / MAX_LEVEL_FLOAT);
+ h -= (int) ((h - ih) * (MAX_LEVEL - level) * mState.mScaleHeight / MAX_LEVEL);
}
final int layoutDirection = getLayoutDirection();
@@ -239,6 +255,7 @@
float mScaleHeight = DO_NOT_SCALE;
int mGravity = Gravity.LEFT;
boolean mUseIntrinsicSizeAsMin = false;
+ int mInitialLevel = 0;
ScaleState(ScaleState orig) {
super(orig);
@@ -248,6 +265,7 @@
mScaleHeight = orig.mScaleHeight;
mGravity = orig.mGravity;
mUseIntrinsicSizeAsMin = orig.mUseIntrinsicSizeAsMin;
+ mInitialLevel = orig.mInitialLevel;
}
}
@@ -257,10 +275,23 @@
}
}
+ /**
+ * Creates a new ScaleDrawable based on the specified constant state.
+ * <p>
+ * The resulting drawable is guaranteed to have a new constant state.
+ *
+ * @param state constant state from which the drawable inherits
+ */
private ScaleDrawable(ScaleState state, Resources res) {
super(state, res);
mState = state;
+
+ updateLocalState();
+ }
+
+ private void updateLocalState() {
+ setLevel(mState.mInitialLevel);
}
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 1747225..1105ca4 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -126,9 +126,13 @@
* <dd>Defines path data using exactly same format as "d" attribute
* in the SVG's path data. This is defined in the viewport space.</dd>
* <dt><code>android:fillColor</code></dt>
- * <dd>Defines the color to fill the path (none if not present).</dd>
+ * <dd>Specifies the color used to fill the path. May be a color or (SDK 24+ only) a color state
+ * list. If this property is animated, any value set by the animation will override the original
+ * value. No path fill is drawn if this property is not specified.</dd>
* <dt><code>android:strokeColor</code></dt>
- * <dd>Defines the color to draw the path outline (none if not present).</dd>
+ * <dd>Specifies the color used to draw the path outline. May be a color or (SDK 24+ only) a color
+ * state list. If this property is animated, any value set by the animation will override the
+ * original value. No path outline is drawn if this property is not specified.</dd>
* <dt><code>android:strokeWidth</code></dt>
* <dd>The width a path stroke.</dd>
* <dt><code>android:strokeAlpha</code></dt>
@@ -374,19 +378,24 @@
@Override
public boolean isStateful() {
- return super.isStateful() || (mVectorState != null && mVectorState.mTint != null
- && mVectorState.mTint.isStateful());
+ return super.isStateful() || (mVectorState != null && mVectorState.isStateful());
}
@Override
protected boolean onStateChange(int[] stateSet) {
+ boolean changed = false;
+
final VectorDrawableState state = mVectorState;
+ if (state.mVPathRenderer != null && state.mVPathRenderer.onStateChange(stateSet)) {
+ changed = true;
+ state.mCacheDirty = true;
+ }
if (state.mTint != null && state.mTintMode != null) {
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
- invalidateSelf();
- return true;
+ changed = true;
}
- return false;
+
+ return changed;
}
@Override
@@ -664,7 +673,7 @@
if (SHAPE_PATH.equals(tagName)) {
final VFullPath path = new VFullPath();
path.inflate(res, attrs, theme);
- currentGroup.mChildren.add(path);
+ currentGroup.addChild(path);
if (path.getPathName() != null) {
pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
}
@@ -673,7 +682,7 @@
} else if (SHAPE_CLIP_PATH.equals(tagName)) {
final VClipPath path = new VClipPath();
path.inflate(res, attrs, theme);
- currentGroup.mChildren.add(path);
+ currentGroup.addChild(path);
if (path.getPathName() != null) {
pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
}
@@ -681,7 +690,7 @@
} else if (SHAPE_GROUP.equals(tagName)) {
VGroup newChildGroup = new VGroup();
newChildGroup.inflate(res, attrs, theme);
- currentGroup.mChildren.add(newChildGroup);
+ currentGroup.addChild(newChildGroup);
groupStack.push(newChildGroup);
if (newChildGroup.getGroupName() != null) {
pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(),
@@ -700,7 +709,7 @@
// Print the tree out for debug.
if (DBG_VECTOR_DRAWABLE) {
- printGroupTree(pathRenderer.mRootGroup, 0);
+ pathRenderer.printGroupTree();
}
if (noPathTag) {
@@ -715,24 +724,6 @@
}
}
- private void printGroupTree(VGroup currentGroup, int level) {
- String indent = "";
- for (int i = 0; i < level; i++) {
- indent += " ";
- }
- // Print the current node
- Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName()
- + " rotation is " + currentGroup.mRotate);
- Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString());
- // Then print all the children groups
- for (int i = 0; i < currentGroup.mChildren.size(); i++) {
- final VObject child = currentGroup.mChildren.get(i);
- if (child instanceof VGroup) {
- printGroupTree((VGroup) child, level + 1);
- }
- }
- }
-
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
@@ -890,6 +881,11 @@
return mChangingConfigurations
| (mTint != null ? mTint.getChangingConfigurations() : 0);
}
+
+ public boolean isStateful() {
+ return (mTint != null && mTint.isStateful())
+ || (mVPathRenderer != null && mVPathRenderer.isStateful());
+ }
}
private static class VPathRenderer {
@@ -972,21 +968,35 @@
mRootGroup.applyTheme(t);
}
+ public boolean onStateChange(int[] stateSet) {
+ return mRootGroup.onStateChange(stateSet);
+ }
+
+ public boolean isStateful() {
+ return mRootGroup.isStateful();
+ }
+
public void draw(Canvas canvas, int w, int h, ColorFilter filter) {
final float scaleX = w / mViewportWidth;
final float scaleY = h / mViewportHeight;
mRootGroup.draw(canvas, mTempState, Matrix.IDENTITY_MATRIX, filter, scaleX, scaleY);
}
+
+ public void printGroupTree() {
+ mRootGroup.printGroupTree("");
+ }
}
private static class VGroup implements VObject {
+ private static final String GROUP_INDENT = " ";
+
// mStackedMatrix is only used temporarily when drawing, it combines all
// the parents' local matrices with the current one.
private final Matrix mStackedMatrix = new Matrix();
/////////////////////////////////////////////////////
// Variables below need to be copied (deep copy if applicable) for mutation.
- final ArrayList<VObject> mChildren = new ArrayList<>();
+ private final ArrayList<VObject> mChildren = new ArrayList<>();
private float mRotate = 0;
private float mPivotX = 0;
@@ -995,6 +1005,7 @@
private float mScaleY = 1;
private float mTranslateX = 0;
private float mTranslateY = 0;
+ private boolean mIsStateful;
// mLocalMatrix is updated based on the update of transformation information,
// either parsed from the XML or by animation.
@@ -1011,6 +1022,7 @@
mScaleY = copy.mScaleY;
mTranslateX = copy.mTranslateX;
mTranslateY = copy.mTranslateY;
+ mIsStateful = copy.mIsStateful;
mThemeAttrs = copy.mThemeAttrs;
mGroupName = copy.mGroupName;
mChangingConfigurations = copy.mChangingConfigurations;
@@ -1054,6 +1066,12 @@
return mLocalMatrix;
}
+ public void addChild(VObject child) {
+ mChildren.add(child);
+
+ mIsStateful |= child.isStateful();
+ }
+
@Override
public void draw(Canvas canvas, TempState temp, Matrix currentMatrix,
ColorFilter filter, float scaleX, float scaleY) {
@@ -1109,6 +1127,26 @@
}
@Override
+ public boolean onStateChange(int[] stateSet) {
+ boolean changed = false;
+
+ final ArrayList<VObject> children = mChildren;
+ for (int i = 0, count = children.size(); i < count; i++) {
+ final VObject child = children.get(i);
+ if (child.isStateful()) {
+ changed |= child.onStateChange(stateSet);
+ }
+ }
+
+ return changed;
+ }
+
+ @Override
+ public boolean isStateful() {
+ return mIsStateful;
+ }
+
+ @Override
public boolean canApplyTheme() {
if (mThemeAttrs != null) {
return true;
@@ -1139,6 +1177,9 @@
final VObject child = children.get(i);
if (child.canApplyTheme()) {
child.applyTheme(t);
+
+ // Applying a theme may have made the child stateful.
+ mIsStateful |= child.isStateful();
}
}
}
@@ -1153,6 +1194,24 @@
mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
}
+ public void printGroupTree(String indent) {
+ Log.v(LOGTAG, indent + "group:" + getGroupName() + " rotation is " + mRotate);
+ Log.v(LOGTAG, indent + "matrix:" + getLocalMatrix().toString());
+
+ final int count = mChildren.size();
+ if (count > 0) {
+ indent += GROUP_INDENT;
+ }
+
+ // Then print all the children groups.
+ for (int i = 0; i < count; i++) {
+ final VObject child = mChildren.get(i);
+ if (child instanceof VGroup) {
+ ((VGroup) child).printGroupTree(indent);
+ }
+ }
+ }
+
/* Setters and Getters, used by animator from AnimatedVectorDrawable. */
@SuppressWarnings("unused")
public float getRotation() {
@@ -1398,6 +1457,16 @@
// No-op.
}
+ @Override
+ public boolean onStateChange(int[] stateSet) {
+ return false;
+ }
+
+ @Override
+ public boolean isStateful() {
+ return false;
+ }
+
private void updateStateFromTypedArray(TypedArray a) {
// Account for any configuration changes.
mChangingConfigurations |= a.getChangingConfigurations();
@@ -1427,9 +1496,11 @@
// Variables below need to be copied (deep copy if applicable) for mutation.
private int[] mThemeAttrs;
+ ColorStateList mStrokeColors = null;
int mStrokeColor = Color.TRANSPARENT;
float mStrokeWidth = 0;
+ ColorStateList mFillColors = null;
int mFillColor = Color.TRANSPARENT;
float mStrokeAlpha = 1.0f;
int mFillRule;
@@ -1448,11 +1519,14 @@
public VFullPath(VFullPath copy) {
super(copy);
+
mThemeAttrs = copy.mThemeAttrs;
+ mStrokeColors = copy.mStrokeColors;
mStrokeColor = copy.mStrokeColor;
mStrokeWidth = copy.mStrokeWidth;
mStrokeAlpha = copy.mStrokeAlpha;
+ mFillColors = copy.mFillColors;
mFillColor = copy.mFillColor;
mFillRule = copy.mFillRule;
mFillAlpha = copy.mFillAlpha;
@@ -1492,6 +1566,31 @@
}
@Override
+ public boolean onStateChange(int[] stateSet) {
+ boolean changed = false;
+
+ if (mStrokeColors != null && mStrokeColors.isStateful()) {
+ final int strokeColor = mStrokeColor;
+ mStrokeColor = mStrokeColors.getColorForState(stateSet, strokeColor);
+ changed |= strokeColor != mStrokeColor;
+ }
+
+ if (mFillColors != null && mFillColors.isStateful()) {
+ final int fillColor = mFillColor;
+ mFillColor = mFillColors.getColorForState(stateSet, fillColor);
+ changed |= fillColor != mFillColor;
+ }
+
+ return changed;
+ }
+
+ @Override
+ public boolean isStateful() {
+ return mStrokeColors != null && mStrokeColors.isStateful()
+ || mFillColors != null && mFillColors.isStateful();
+ }
+
+ @Override
public void toPath(TempState temp, Path path) {
super.toPath(temp, path);
@@ -1614,8 +1713,20 @@
mNodes = PathParser.createNodesFromPathData(pathData);
}
- mFillColor = a.getColor(R.styleable.VectorDrawablePath_fillColor,
- mFillColor);
+ final ColorStateList fillColors = a.getColorStateList(
+ R.styleable.VectorDrawablePath_fillColor);
+ if (fillColors != null) {
+ mFillColors = fillColors;
+ mFillColor = fillColors.getDefaultColor();
+ }
+
+ final ColorStateList strokeColors = a.getColorStateList(
+ R.styleable.VectorDrawablePath_strokeColor);
+ if (strokeColors != null) {
+ mStrokeColors = strokeColors;
+ mStrokeColor = strokeColors.getDefaultColor();
+ }
+
mFillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha,
mFillAlpha);
mStrokeLineCap = getStrokeLineCap(a.getInt(
@@ -1624,8 +1735,6 @@
R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin);
mStrokeMiterlimit = a.getFloat(
R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit);
- mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_strokeColor,
- mStrokeColor);
mStrokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha,
mStrokeAlpha);
mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth,
@@ -1662,6 +1771,7 @@
@SuppressWarnings("unused")
void setStrokeColor(int strokeColor) {
+ mStrokeColors = null;
mStrokeColor = strokeColor;
}
@@ -1692,6 +1802,7 @@
@SuppressWarnings("unused")
void setFillColor(int fillColor) {
+ mFillColors = null;
mFillColor = fillColor;
}
@@ -1752,5 +1863,7 @@
void inflate(Resources r, AttributeSet attrs, Theme theme);
boolean canApplyTheme();
void applyTheme(Theme t);
+ boolean onStateChange(int[] state);
+ boolean isStateful();
}
}
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 5d777b0..c8333c8 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -217,13 +217,22 @@
* Returns {@code true} if there was at least one of those types.
*/
public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias) {
+ return deleteAllTypesForAlias(keystore, alias, KeyStore.UID_SELF);
+ }
+
+ /**
+ * Delete all types (private key, certificate, CA certificate) for a
+ * particular {@code alias}. All three can exist for any given alias.
+ * Returns {@code true} if there was at least one of those types.
+ */
+ public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias, int uid) {
/*
* Make sure every type is deleted. There can be all three types, so
* don't use a conditional here.
*/
- return keystore.delete(Credentials.USER_PRIVATE_KEY + alias)
- | keystore.delete(Credentials.USER_SECRET_KEY + alias)
- | deleteCertificateTypesForAlias(keystore, alias);
+ return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid)
+ | keystore.delete(Credentials.USER_SECRET_KEY + alias, uid)
+ | deleteCertificateTypesForAlias(keystore, alias, uid);
}
/**
@@ -232,12 +241,21 @@
* Returns {@code true} if there was at least one of those types.
*/
public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias) {
+ return deleteCertificateTypesForAlias(keystore, alias, KeyStore.UID_SELF);
+ }
+
+ /**
+ * Delete all types (private key, certificate, CA certificate) for a
+ * particular {@code alias}. All three can exist for any given alias.
+ * Returns {@code true} if there was at least one of those types.
+ */
+ public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias, int uid) {
/*
* Make sure every certificate type is deleted. There can be two types,
* so don't use a conditional here.
*/
- return keystore.delete(Credentials.USER_CERTIFICATE + alias)
- | keystore.delete(Credentials.CA_CERTIFICATE + alias);
+ return keystore.delete(Credentials.USER_CERTIFICATE + alias, uid)
+ | keystore.delete(Credentials.CA_CERTIFICATE + alias, uid);
}
/**
@@ -245,7 +263,15 @@
* Returns {@code true} if an entry was was deleted.
*/
static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias) {
- return keystore.delete(Credentials.USER_PRIVATE_KEY + alias);
+ return deletePrivateKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
+ }
+
+ /**
+ * Delete private key for a particular {@code alias}.
+ * Returns {@code true} if an entry was was deleted.
+ */
+ static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+ return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid);
}
/**
@@ -253,6 +279,14 @@
* Returns {@code true} if an entry was was deleted.
*/
public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias) {
- return keystore.delete(Credentials.USER_SECRET_KEY + alias);
+ return deleteSecretKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
+ }
+
+ /**
+ * Delete secret key for a particular {@code alias}.
+ * Returns {@code true} if an entry was was deleted.
+ */
+ public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+ return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
}
}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 7de26d6..5b2594d 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -374,7 +374,7 @@
throw new KeyChainException("keystore had a problem");
}
return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
- KeyStore.getInstance(), keyId);
+ KeyStore.getInstance(), keyId, KeyStore.UID_SELF);
} catch (RemoteException e) {
throw new KeyChainException(e);
} catch (RuntimeException e) {
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 98b44dc..1b87a41 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -155,15 +155,19 @@
return state() == State.UNLOCKED;
}
- public byte[] get(String key) {
+ public byte[] get(String key, int uid) {
try {
- return mBinder.get(key);
+ return mBinder.get(key, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
}
}
+ public byte[] get(String key) {
+ return get(key, UID_SELF);
+ }
+
public boolean put(String key, byte[] value, int uid, int flags) {
return insert(key, value, uid, flags) == NO_ERROR;
}
@@ -348,9 +352,9 @@
* Returns the last modification time of the key in milliseconds since the
* epoch. Will return -1L if the key could not be found or other error.
*/
- public long getmtime(String key) {
+ public long getmtime(String key, int uid) {
try {
- final long millis = mBinder.getmtime(key);
+ final long millis = mBinder.getmtime(key, uid);
if (millis == -1L) {
return -1L;
}
@@ -362,6 +366,10 @@
}
}
+ public long getmtime(String key) {
+ return getmtime(key, UID_SELF);
+ }
+
public boolean duplicate(String srcKey, int srcUid, String destKey, int destUid) {
try {
return mBinder.duplicate(srcKey, srcUid, destKey, destUid) == NO_ERROR;
@@ -423,15 +431,20 @@
}
public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
- KeyCharacteristics outCharacteristics) {
+ int uid, KeyCharacteristics outCharacteristics) {
try {
- return mBinder.getKeyCharacteristics(alias, clientId, appId, outCharacteristics);
+ return mBinder.getKeyCharacteristics(alias, clientId, appId, uid, outCharacteristics);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
}
}
+ public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
+ KeyCharacteristics outCharacteristics) {
+ return getKeyCharacteristics(alias, clientId, appId, UID_SELF, outCharacteristics);
+ }
+
public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData,
int uid, int flags, KeyCharacteristics outCharacteristics) {
try {
@@ -449,9 +462,23 @@
}
public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
- KeymasterBlob appId) {
+ KeymasterBlob appId, int uid) {
try {
- return mBinder.exportKey(alias, format, clientId, appId);
+ return mBinder.exportKey(alias, format, clientId, appId, uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return null;
+ }
+ }
+ public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
+ KeymasterBlob appId) {
+ return exportKey(alias, format, clientId, appId, UID_SELF);
+ }
+
+ public OperationResult begin(String alias, int purpose, boolean pruneable,
+ KeymasterArguments args, byte[] entropy, int uid) {
+ try {
+ return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
@@ -460,12 +487,7 @@
public OperationResult begin(String alias, int purpose, boolean pruneable,
KeymasterArguments args, byte[] entropy) {
- try {
- return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return null;
- }
+ return begin(alias, purpose, pruneable, args, entropy, UID_SELF);
}
public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {
@@ -640,7 +662,7 @@
* {@link KeyStoreException}.
*/
public InvalidKeyException getInvalidKeyException(
- String keystoreKeyAlias, KeyStoreException e) {
+ String keystoreKeyAlias, int uid, KeyStoreException e) {
switch (e.getErrorCode()) {
case LOCKED:
return new UserNotAuthenticatedException();
@@ -658,7 +680,8 @@
// to authenticate.
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int getKeyCharacteristicsErrorCode =
- getKeyCharacteristics(keystoreKeyAlias, null, null, keyCharacteristics);
+ getKeyCharacteristics(keystoreKeyAlias, null, null, uid,
+ keyCharacteristics);
if (getKeyCharacteristicsErrorCode != NO_ERROR) {
return new InvalidKeyException(
"Failed to obtained key characteristics",
@@ -708,7 +731,8 @@
* Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error
* code.
*/
- public InvalidKeyException getInvalidKeyException(String keystoreKeyAlias, int errorCode) {
- return getInvalidKeyException(keystoreKeyAlias, getKeyStoreException(errorCode));
+ public InvalidKeyException getInvalidKeyException(String keystoreKeyAlias, int uid,
+ int errorCode) {
+ return getInvalidKeyException(keystoreKeyAlias, uid, getKeyStoreException(errorCode));
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 38cacd0..042dc83 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -249,7 +249,8 @@
purpose,
true, // permit aborting this operation if keystore runs out of resources
keymasterInputArgs,
- additionalEntropy);
+ additionalEntropy,
+ mKey.getUid());
if (opResult == null) {
throw new KeyStoreConnectException();
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
index 10aab7e..45f2110 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
@@ -155,9 +155,9 @@
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = getKeyStore().getKeyCharacteristics(
- key.getAlias(), null, null, keyCharacteristics);
+ key.getAlias(), null, null, key.getUid(), keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
- throw getKeyStore().getInvalidKeyException(key.getAlias(), errorCode);
+ throw getKeyStore().getInvalidKeyException(key.getAlias(), key.getUid(), errorCode);
}
long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
if (keySizeBits == -1) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java
index 5dbcd68..aa7bdff 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java
@@ -28,8 +28,8 @@
public class AndroidKeyStoreECPrivateKey extends AndroidKeyStorePrivateKey implements ECKey {
private final ECParameterSpec mParams;
- public AndroidKeyStoreECPrivateKey(String alias, ECParameterSpec params) {
- super(alias, KeyProperties.KEY_ALGORITHM_EC);
+ public AndroidKeyStoreECPrivateKey(String alias, int uid, ECParameterSpec params) {
+ super(alias, uid, KeyProperties.KEY_ALGORITHM_EC);
mParams = params;
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
index 3ed396d..2efaeb6 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
@@ -30,15 +30,15 @@
private final ECParameterSpec mParams;
private final ECPoint mW;
- public AndroidKeyStoreECPublicKey(String alias, byte[] x509EncodedForm, ECParameterSpec params,
+ public AndroidKeyStoreECPublicKey(String alias, int uid, byte[] x509EncodedForm, ECParameterSpec params,
ECPoint w) {
- super(alias, KeyProperties.KEY_ALGORITHM_EC, x509EncodedForm);
+ super(alias, uid, KeyProperties.KEY_ALGORITHM_EC, x509EncodedForm);
mParams = params;
mW = w;
}
- public AndroidKeyStoreECPublicKey(String alias, ECPublicKey info) {
- this(alias, info.getEncoded(), info.getParams(), info.getW());
+ public AndroidKeyStoreECPublicKey(String alias, int uid, ECPublicKey info) {
+ this(alias, uid, info.getEncoded(), info.getParams(), info.getW());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
index d20e3af..2e8ac32 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
@@ -168,7 +168,8 @@
KeymasterDefs.KM_PURPOSE_SIGN,
true,
keymasterArgs,
- null); // no additional entropy needed for HMAC because it's deterministic
+ null, // no additional entropy needed for HMAC because it's deterministic
+ mKey.getUid());
if (opResult == null) {
throw new KeyStoreConnectException();
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
index e76802f..e8e6310 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
@@ -25,10 +25,12 @@
*/
public class AndroidKeyStoreKey implements Key {
private final String mAlias;
+ private final int mUid;
private final String mAlgorithm;
- public AndroidKeyStoreKey(String alias, String algorithm) {
+ public AndroidKeyStoreKey(String alias, int uid, String algorithm) {
mAlias = alias;
+ mUid = uid;
mAlgorithm = algorithm;
}
@@ -36,6 +38,10 @@
return mAlias;
}
+ int getUid() {
+ return mUid;
+ }
+
@Override
public String getAlgorithm() {
return mAlgorithm;
@@ -59,6 +65,7 @@
int result = 1;
result = prime * result + ((mAlgorithm == null) ? 0 : mAlgorithm.hashCode());
result = prime * result + ((mAlias == null) ? 0 : mAlias.hashCode());
+ result = prime * result + mUid;
return result;
}
@@ -88,6 +95,9 @@
} else if (!mAlias.equals(other.mAlias)) {
return false;
}
+ if (mUid != other.mUid) {
+ return false;
+ }
return true;
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
index 5ce4fd2..303b0f2 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
@@ -62,7 +62,8 @@
"Unsupported key type: " + key.getClass().getName()
+ ". KeyInfo can be obtained only for Android Keystore private keys");
}
- String keyAliasInKeystore = ((AndroidKeyStorePrivateKey) key).getAlias();
+ AndroidKeyStorePrivateKey keystorePrivateKey = (AndroidKeyStorePrivateKey) key;
+ String keyAliasInKeystore = keystorePrivateKey.getAlias();
String entryAlias;
if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
@@ -71,7 +72,7 @@
}
@SuppressWarnings("unchecked")
T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(
- mKeyStore, entryAlias, keyAliasInKeystore);
+ mKeyStore, entryAlias, keyAliasInKeystore, keystorePrivateKey.getUid());
return result;
} else if (X509EncodedKeySpec.class.equals(keySpecClass)) {
if (!(key instanceof AndroidKeyStorePublicKey)) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 4c174f1..e6276a4 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -297,11 +297,12 @@
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
boolean success = false;
try {
- Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias());
+ Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias(), spec.getUid());
int errorCode = mKeyStore.generateKey(
keyAliasInKeystore,
args,
additionalEntropy,
+ spec.getUid(),
flags,
resultingKeyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
@@ -315,12 +316,14 @@
} catch (IllegalArgumentException e) {
throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
}
- SecretKey result = new AndroidKeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
+ SecretKey result = new AndroidKeyStoreSecretKey(
+ keyAliasInKeystore, spec.getUid(), keyAlgorithmJCA);
success = true;
return result;
} finally {
if (!success) {
- Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias());
+ Credentials.deleteAllTypesForAlias(
+ mKeyStore, spec.getKeystoreAlias(), spec.getUid());
}
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 79095f4..65460b5 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -147,6 +147,7 @@
private KeyGenParameterSpec mSpec;
private String mEntryAlias;
+ private int mEntryUid;
private boolean mEncryptionAtRestRequired;
private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
private int mKeymasterAlgorithm = -1;
@@ -283,6 +284,7 @@
}
mEntryAlias = spec.getKeystoreAlias();
+ mEntryUid = spec.getUid();
mSpec = spec;
mKeymasterAlgorithm = keymasterAlgorithm;
mEncryptionAtRestRequired = encryptionAtRestRequired;
@@ -352,6 +354,7 @@
private void resetAll() {
mEntryAlias = null;
+ mEntryUid = KeyStore.UID_SELF;
mJcaKeyAlgorithm = null;
mKeymasterAlgorithm = -1;
mKeymasterPurposes = null;
@@ -470,12 +473,13 @@
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias;
boolean success = false;
try {
- Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
int errorCode = mKeyStore.generateKey(
privateKeyAlias,
args,
additionalEntropy,
+ mEntryUid,
flags,
resultingKeyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
@@ -486,7 +490,7 @@
KeyPair result;
try {
result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
- mKeyStore, privateKeyAlias);
+ mKeyStore, privateKeyAlias, mEntryUid);
} catch (UnrecoverableKeyException e) {
throw new ProviderException("Failed to load generated key pair from keystore", e);
}
@@ -515,7 +519,7 @@
int insertErrorCode = mKeyStore.insert(
Credentials.USER_CERTIFICATE + mEntryAlias,
certBytes,
- KeyStore.UID_SELF,
+ mEntryUid,
flags);
if (insertErrorCode != KeyStore.NO_ERROR) {
throw new ProviderException("Failed to store self-signed certificate",
@@ -526,7 +530,7 @@
return result;
} finally {
if (!success) {
- Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
}
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreLoadStoreParameter.java b/keystore/java/android/security/keystore/AndroidKeyStoreLoadStoreParameter.java
new file mode 100644
index 0000000..45d579e
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreLoadStoreParameter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import java.security.KeyStore;
+import java.security.KeyStore.ProtectionParameter;
+
+class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter {
+
+ private final int mUid;
+
+ AndroidKeyStoreLoadStoreParameter(int uid) {
+ mUid = uid;
+ }
+
+ @Override
+ public ProtectionParameter getProtectionParameter() {
+ return null;
+ }
+
+ int getUid() {
+ return mUid;
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
index b586ad4..06e4c88 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
@@ -25,7 +25,7 @@
*/
public class AndroidKeyStorePrivateKey extends AndroidKeyStoreKey implements PrivateKey {
- public AndroidKeyStorePrivateKey(String alias, String algorithm) {
- super(alias, algorithm);
+ public AndroidKeyStorePrivateKey(String alias, int uid, String algorithm) {
+ super(alias, uid, algorithm);
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index ba39ba7..c31a8b7 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -22,15 +22,19 @@
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
+import java.io.IOException;
import java.security.KeyFactory;
import java.security.KeyPair;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
import java.security.interfaces.ECKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAKey;
@@ -167,6 +171,7 @@
@NonNull
public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey(
@NonNull String alias,
+ int uid,
@NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm,
@NonNull byte[] x509EncodedForm) {
PublicKey publicKey;
@@ -180,9 +185,9 @@
throw new ProviderException("Invalid X.509 encoding of public key", e);
}
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- return new AndroidKeyStoreECPublicKey(alias, (ECPublicKey) publicKey);
+ return new AndroidKeyStoreECPublicKey(alias, uid, (ECPublicKey) publicKey);
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- return new AndroidKeyStoreRSAPublicKey(alias, (RSAPublicKey) publicKey);
+ return new AndroidKeyStoreRSAPublicKey(alias, uid, (RSAPublicKey) publicKey);
} else {
throw new ProviderException("Unsupported Android Keystore public key algorithm: "
+ keyAlgorithm);
@@ -195,10 +200,10 @@
String keyAlgorithm = publicKey.getAlgorithm();
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
return new AndroidKeyStoreECPrivateKey(
- publicKey.getAlias(), ((ECKey) publicKey).getParams());
+ publicKey.getAlias(), publicKey.getUid(), ((ECKey) publicKey).getParams());
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
return new AndroidKeyStoreRSAPrivateKey(
- publicKey.getAlias(), ((RSAKey) publicKey).getModulus());
+ publicKey.getAlias(), publicKey.getUid(), ((RSAKey) publicKey).getModulus());
} else {
throw new ProviderException("Unsupported Android Keystore public key algorithm: "
+ keyAlgorithm);
@@ -207,18 +212,18 @@
@NonNull
public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias)
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = keyStore.getKeyCharacteristics(
- privateKeyAlias, null, null, keyCharacteristics);
+ privateKeyAlias, null, null, uid, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to obtain information about private key")
.initCause(KeyStore.getKeyStoreException(errorCode));
}
ExportResult exportResult = keyStore.exportKey(
- privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
+ privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid);
if (exportResult.resultCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to obtain X.509 form of public key")
@@ -242,15 +247,15 @@
}
return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
- privateKeyAlias, jcaKeyAlgorithm, x509EncodedPublicKey);
+ privateKeyAlias, uid, jcaKeyAlgorithm, x509EncodedPublicKey);
}
@NonNull
public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias)
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
AndroidKeyStorePublicKey publicKey =
- loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias);
+ loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid);
AndroidKeyStorePrivateKey privateKey =
AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey);
return new KeyPair(publicKey, privateKey);
@@ -258,19 +263,19 @@
@NonNull
public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias)
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
- KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias);
+ KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid);
return (AndroidKeyStorePrivateKey) keyPair.getPrivate();
}
@NonNull
public static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String secretKeyAlias)
+ @NonNull KeyStore keyStore, @NonNull String secretKeyAlias, int uid)
throws UnrecoverableKeyException {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = keyStore.getKeyCharacteristics(
- secretKeyAlias, null, null, keyCharacteristics);
+ secretKeyAlias, null, null, uid, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to obtain information about key")
@@ -301,6 +306,29 @@
new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
}
- return new AndroidKeyStoreSecretKey(secretKeyAlias, keyAlgorithmString);
+ return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString);
+ }
+
+ /**
+ * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
+ * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
+ * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN)
+ * all of which are system.
+ *
+ * <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is
+ * no need to invoke {@code load} on it.
+ */
+ @NonNull
+ public static java.security.KeyStore getKeyStoreForUid(int uid)
+ throws KeyStoreException, NoSuchProviderException {
+ java.security.KeyStore result =
+ java.security.KeyStore.getInstance("AndroidKeyStore", PROVIDER_NAME);
+ try {
+ result.load(new AndroidKeyStoreLoadStoreParameter(uid));
+ } catch (NoSuchAlgorithmException | CertificateException | IOException e) {
+ throw new KeyStoreException(
+ "Failed to load AndroidKeyStore KeyStore for UID " + uid, e);
+ }
+ return result;
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
index 9fea30d..4194780 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
@@ -28,8 +28,8 @@
private final byte[] mEncoded;
- public AndroidKeyStorePublicKey(String alias, String algorithm, byte[] x509EncodedForm) {
- super(alias, algorithm);
+ public AndroidKeyStorePublicKey(String alias, int uid, String algorithm, byte[] x509EncodedForm) {
+ super(alias, uid, algorithm);
mEncoded = ArrayUtils.cloneIfNotEmpty(x509EncodedForm);
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
index 56cc44c..2ae68fa 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
@@ -415,9 +415,10 @@
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = getKeyStore().getKeyCharacteristics(
- keystoreKey.getAlias(), null, null, keyCharacteristics);
+ keystoreKey.getAlias(), null, null, keystoreKey.getUid(), keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
- throw getKeyStore().getInvalidKeyException(keystoreKey.getAlias(), errorCode);
+ throw getKeyStore().getInvalidKeyException(
+ keystoreKey.getAlias(), keystoreKey.getUid(), errorCode);
}
long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
if (keySizeBits == -1) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java
index 179ffd8..adb3922 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java
@@ -29,8 +29,8 @@
private final BigInteger mModulus;
- public AndroidKeyStoreRSAPrivateKey(String alias, BigInteger modulus) {
- super(alias, KeyProperties.KEY_ALGORITHM_RSA);
+ public AndroidKeyStoreRSAPrivateKey(String alias, int uid, BigInteger modulus) {
+ super(alias, uid, KeyProperties.KEY_ALGORITHM_RSA);
mModulus = modulus;
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
index 08a173e..d85aace 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
@@ -28,15 +28,15 @@
private final BigInteger mModulus;
private final BigInteger mPublicExponent;
- public AndroidKeyStoreRSAPublicKey(String alias, byte[] x509EncodedForm, BigInteger modulus,
+ public AndroidKeyStoreRSAPublicKey(String alias, int uid, byte[] x509EncodedForm, BigInteger modulus,
BigInteger publicExponent) {
- super(alias, KeyProperties.KEY_ALGORITHM_RSA, x509EncodedForm);
+ super(alias, uid, KeyProperties.KEY_ALGORITHM_RSA, x509EncodedForm);
mModulus = modulus;
mPublicExponent = publicExponent;
}
- public AndroidKeyStoreRSAPublicKey(String alias, RSAPublicKey info) {
- this(alias, info.getEncoded(), info.getModulus(), info.getPublicExponent());
+ public AndroidKeyStoreRSAPublicKey(String alias, int uid, RSAPublicKey info) {
+ this(alias, uid, info.getEncoded(), info.getModulus(), info.getPublicExponent());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
index af354ab..b8e6af7 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
@@ -25,7 +25,7 @@
*/
public class AndroidKeyStoreSecretKey extends AndroidKeyStoreKey implements SecretKey {
- public AndroidKeyStoreSecretKey(String alias, String algorithm) {
- super(alias, algorithm);
+ public AndroidKeyStoreSecretKey(String alias, int uid, String algorithm) {
+ super(alias, uid, algorithm);
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 11c22a9..8d606bf 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -59,7 +59,8 @@
if (!KeyInfo.class.equals(keySpecClass)) {
throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
}
- String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias();
+ AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
+ String keyAliasInKeystore = keystoreKey.getAlias();
String entryAlias;
if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
@@ -67,13 +68,14 @@
throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
}
- return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore);
+ return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid());
}
- static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore) {
+ static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore,
+ int keyUid) {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode =
- keyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
+ int errorCode = keyStore.getKeyCharacteristics(
+ keyAliasInKeystore, null, null, keyUid, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw new ProviderException("Failed to obtain information about key."
+ " Keystore error: " + errorCode);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
index 76240dd..da47b6b 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
@@ -204,8 +204,8 @@
mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
true, // permit aborting this operation if keystore runs out of resources
keymasterInputArgs,
- null // no additional entropy for begin -- only finish might need some
- );
+ null, // no additional entropy for begin -- only finish might need some
+ mKey.getUid());
if (opResult == null) {
throw new KeyStoreConnectException();
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index d300a92..cdcc7a2 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -17,7 +17,6 @@
package android.security.keystore;
import libcore.util.EmptyArray;
-
import android.security.Credentials;
import android.security.KeyStore;
import android.security.KeyStoreParameter;
@@ -34,6 +33,7 @@
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyStore.Entry;
+import java.security.KeyStore.LoadStoreParameter;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStore.SecretKeyEntry;
@@ -84,6 +84,7 @@
public static final String NAME = "AndroidKeyStore";
private KeyStore mKeyStore;
+ private int mUid = KeyStore.UID_SELF;
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
@@ -91,11 +92,11 @@
if (isPrivateKeyEntry(alias)) {
String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
- mKeyStore, privateKeyAlias);
+ mKeyStore, privateKeyAlias, mUid);
} else if (isSecretKeyEntry(alias)) {
String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
- mKeyStore, secretKeyAlias);
+ mKeyStore, secretKeyAlias, mUid);
} else {
// Key not found
return null;
@@ -115,7 +116,7 @@
final Certificate[] caList;
- final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+ final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
if (caBytes != null) {
final Collection<X509Certificate> caChain = toCertificates(caBytes);
@@ -141,12 +142,12 @@
throw new NullPointerException("alias == null");
}
- byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+ byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
if (encodedCert != null) {
return getCertificateForPrivateKeyEntry(alias, encodedCert);
}
- encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+ encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
if (encodedCert != null) {
return getCertificateForTrustedCertificateEntry(encodedCert);
}
@@ -183,13 +184,13 @@
}
String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- if (mKeyStore.contains(privateKeyAlias)) {
+ if (mKeyStore.contains(privateKeyAlias, mUid)) {
// As expected, keystore contains the private key corresponding to this public key. Wrap
// the certificate so that its getPublicKey method returns an Android Keystore
// PublicKey. This key will delegate crypto operations involving this public key to
// Android Keystore when higher-priority providers do not offer these crypto
// operations for this key.
- return wrapIntoKeyStoreCertificate(privateKeyAlias, cert);
+ return wrapIntoKeyStoreCertificate(privateKeyAlias, mUid, cert);
} else {
// This KeyStore entry/alias is supposed to contain the private key corresponding to
// the public key in this certificate, but it does not for some reason. It's probably a
@@ -206,9 +207,9 @@
* find out which key alias to use. These operations cannot work without an alias.
*/
private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate(
- String privateKeyAlias, X509Certificate certificate) {
+ String privateKeyAlias, int uid, X509Certificate certificate) {
return (certificate != null)
- ? new KeyStoreX509Certificate(privateKeyAlias, certificate) : null;
+ ? new KeyStoreX509Certificate(privateKeyAlias, uid, certificate) : null;
}
private static X509Certificate toCertificate(byte[] bytes) {
@@ -235,7 +236,7 @@
}
private Date getModificationDate(String alias) {
- final long epochMillis = mKeyStore.getmtime(alias);
+ final long epochMillis = mKeyStore.getmtime(alias, mUid);
if (epochMillis == -1L) {
return null;
}
@@ -516,13 +517,14 @@
if (shouldReplacePrivateKey) {
// Delete the stored private key and any related entries before importing the
// provided key
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
int errorCode = mKeyStore.importKey(
Credentials.USER_PRIVATE_KEY + alias,
importArgs,
KeymasterDefs.KM_KEY_FORMAT_PKCS8,
pkcs8EncodedPrivateKeyBytes,
+ mUid,
flags,
resultingKeyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
@@ -531,13 +533,13 @@
}
} else {
// Keep the stored private key around -- delete all other entry types
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
}
// Store the leaf certificate
int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
- KeyStore.UID_SELF, flags);
+ mUid, flags);
if (errorCode != KeyStore.NO_ERROR) {
throw new KeyStoreException("Failed to store certificate #0",
KeyStore.getKeyStoreException(errorCode));
@@ -545,7 +547,7 @@
// Store the certificate chain
errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
- KeyStore.UID_SELF, flags);
+ mUid, flags);
if (errorCode != KeyStore.NO_ERROR) {
throw new KeyStoreException("Failed to store certificate chain",
KeyStore.getKeyStoreException(errorCode));
@@ -554,10 +556,10 @@
} finally {
if (!success) {
if (shouldReplacePrivateKey) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
} else {
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
}
}
}
@@ -712,13 +714,14 @@
throw new KeyStoreException(e);
}
- Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
int errorCode = mKeyStore.importKey(
keyAliasInKeystore,
args,
KeymasterDefs.KM_KEY_FORMAT_RAW,
keyMaterial,
+ mUid,
0, // flags
new KeyCharacteristics());
if (errorCode != KeyStore.NO_ERROR) {
@@ -751,8 +754,7 @@
throw new KeyStoreException(e);
}
- if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
- KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
+ if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) {
throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
}
}
@@ -764,13 +766,13 @@
}
// At least one entry corresponding to this alias exists in keystore
- if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
+ if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) {
throw new KeyStoreException("Failed to delete entry: " + alias);
}
}
private Set<String> getUniqueAliases() {
- final String[] rawAliases = mKeyStore.list("");
+ final String[] rawAliases = mKeyStore.list("", mUid);
if (rawAliases == null) {
return new HashSet<String>();
}
@@ -800,10 +802,10 @@
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
- || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias)
- || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
- || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid)
+ || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid)
+ || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid)
+ || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
}
@Override
@@ -825,7 +827,7 @@
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
+ return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid);
}
private boolean isSecretKeyEntry(String alias) {
@@ -833,7 +835,7 @@
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias);
+ return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
}
private boolean isCertificateEntry(String alias) {
@@ -841,7 +843,7 @@
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
}
@Override
@@ -876,10 +878,10 @@
* equivalent to the USER_CERTIFICATE prefix for the Android keystore
* convention.
*/
- final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE);
+ final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid);
if (certAliases != null) {
for (String alias : certAliases) {
- final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+ final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
if (certBytes == null) {
continue;
}
@@ -896,14 +898,14 @@
* Look at all the TrustedCertificateEntry types. Skip all the
* PrivateKeyEntry we looked at above.
*/
- final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE);
+ final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid);
if (certAliases != null) {
for (String alias : caAliases) {
if (nonCaEntries.contains(alias)) {
continue;
}
- final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+ final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
if (certBytes == null) {
continue;
}
@@ -936,6 +938,23 @@
// Unfortunate name collision.
mKeyStore = KeyStore.getInstance();
+ mUid = KeyStore.UID_SELF;
+ }
+
+ @Override
+ public void engineLoad(LoadStoreParameter param) throws IOException,
+ NoSuchAlgorithmException, CertificateException {
+ int uid = KeyStore.UID_SELF;
+ if (param != null) {
+ if (param instanceof AndroidKeyStoreLoadStoreParameter) {
+ uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid();
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported param type: " + param.getClass());
+ }
+ }
+ mKeyStore = KeyStore.getInstance();
+ mUid = uid;
}
@Override
@@ -945,7 +964,7 @@
throw new KeyStoreException("entry == null");
}
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
java.security.KeyStore.TrustedCertificateEntry trE =
@@ -976,16 +995,20 @@
*/
static class KeyStoreX509Certificate extends DelegatingX509Certificate {
private final String mPrivateKeyAlias;
- KeyStoreX509Certificate(String privateKeyAlias, X509Certificate delegate) {
+ private final int mPrivateKeyUid;
+ KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid,
+ X509Certificate delegate) {
super(delegate);
mPrivateKeyAlias = privateKeyAlias;
+ mPrivateKeyUid = privateKeyUid;
}
@Override
public PublicKey getPublicKey() {
PublicKey original = super.getPublicKey();
return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
- mPrivateKeyAlias, original.getAlgorithm(), original.getEncoded());
+ mPrivateKeyAlias, mPrivateKeyUid,
+ original.getAlgorithm(), original.getEncoded());
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index f42d750..add199f 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.security.KeyStore;
import android.text.TextUtils;
import java.math.BigInteger;
@@ -231,6 +232,7 @@
private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
private final String mKeystoreAlias;
+ private final int mUid;
private final int mKeySize;
private final AlgorithmParameterSpec mSpec;
private final X500Principal mCertificateSubject;
@@ -254,6 +256,7 @@
*/
public KeyGenParameterSpec(
String keyStoreAlias,
+ int uid,
int keySize,
AlgorithmParameterSpec spec,
X500Principal certificateSubject,
@@ -293,6 +296,7 @@
}
mKeystoreAlias = keyStoreAlias;
+ mUid = uid;
mKeySize = keySize;
mSpec = spec;
mCertificateSubject = certificateSubject;
@@ -323,6 +327,16 @@
}
/**
+ * Returns the UID which will own the key. {@code -1} is an alias for the UID of the current
+ * process.
+ *
+ * @hide
+ */
+ public int getUid() {
+ return mUid;
+ }
+
+ /**
* Returns the requested key size. If {@code -1}, the size should be looked up from
* {@link #getAlgorithmParameterSpec()}, if provided, otherwise an algorithm-specific default
* size should be used.
@@ -531,6 +545,7 @@
private final String mKeystoreAlias;
private @KeyProperties.PurposeEnum int mPurposes;
+ private int mUid = KeyStore.UID_SELF;
private int mKeySize = -1;
private AlgorithmParameterSpec mSpec;
private X500Principal mCertificateSubject;
@@ -575,6 +590,19 @@
}
/**
+ * Sets the UID which will own the key.
+ *
+ * @param uid UID or {@code -1} for the UID of the current process.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setUid(int uid) {
+ mUid = uid;
+ return this;
+ }
+
+ /**
* Sets the size (in bits) of the key to be generated. For instance, for RSA keys this sets
* the modulus size, for EC keys this selects a curve with a matching field size, and for
* symmetric keys this sets the size of the bitstring which is their key material.
@@ -936,6 +964,7 @@
public KeyGenParameterSpec build() {
return new KeyGenParameterSpec(
mKeystoreAlias,
+ mUid,
mKeySize,
mSpec,
mCertificateSubject,
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java
index 27c1b2a..773729e 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java
@@ -51,7 +51,7 @@
// An error occured. However, some errors should not lead to init throwing an exception.
// See below.
InvalidKeyException e =
- keyStore.getInvalidKeyException(key.getAlias(), beginOpResultCode);
+ keyStore.getInvalidKeyException(key.getAlias(), key.getUid(), beginOpResultCode);
switch (beginOpResultCode) {
case KeyStore.OP_AUTH_NEEDED:
// Operation needs to be authorized by authenticating the user. Don't throw an
diff --git a/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java
index e5c15c5..1af0b7d 100644
--- a/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java
+++ b/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java
@@ -384,6 +384,7 @@
pubKey,
AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
Credentials.USER_PRIVATE_KEY + alias,
+ KeyStore.UID_SELF,
x509userCert.getPublicKey().getAlgorithm(),
x509userCert.getPublicKey().getEncoded()));
diff --git a/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java
index c3b731b..aa718dc 100644
--- a/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java
@@ -1918,7 +1918,7 @@
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
KeyPair keyPair = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
- keyStore, privateKeyAlias);
+ keyStore, privateKeyAlias, KeyStore.UID_SELF);
final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
certGen.setPublicKey(keyPair.getPublic());
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 3eb13ab..cc0943f 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -24,6 +24,7 @@
utils/GLUtils.cpp \
utils/LinearAllocator.cpp \
utils/NinePatchImpl.cpp \
+ utils/StringUtils.cpp \
AmbientShadow.cpp \
AnimationContext.cpp \
Animator.cpp \
@@ -168,7 +169,8 @@
unit_tests/CanvasStateTests.cpp \
unit_tests/ClipAreaTests.cpp \
unit_tests/DamageAccumulatorTests.cpp \
- unit_tests/LinearAllocatorTests.cpp
+ unit_tests/LinearAllocatorTests.cpp \
+ unit_tests/StringUtilsTests.cpp
include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index ff71313..7c63e31 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -54,7 +54,6 @@
, mInitialized(false) {
INIT_LOGD("Creating OpenGL renderer caches");
init();
- initFont();
initConstraints();
initStaticProperties();
initExtensions();
@@ -78,10 +77,6 @@
return true;
}
-void Caches::initFont() {
- fontRenderer = GammaFontRenderer::createRenderer();
-}
-
void Caches::initExtensions() {
if (mExtensions.hasDebugMarker()) {
eventMark = glInsertEventMarkerEXT;
@@ -100,15 +95,9 @@
}
void Caches::initStaticProperties() {
- gpuPixelBuffersEnabled = false;
-
// OpenGL ES 3.0+ specific features
- if (mExtensions.hasPixelBufferObjects()) {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "true") > 0) {
- gpuPixelBuffersEnabled = !strcmp(property, "true");
- }
- }
+ gpuPixelBuffersEnabled = mExtensions.hasPixelBufferObjects()
+ && property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true);
}
void Caches::terminate() {
@@ -203,14 +192,14 @@
dropShadowCache.getMaxSize());
log.appendFormat(" PatchCache %8d / %8d\n",
patchCache.getSize(), patchCache.getMaxSize());
- for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
- const uint32_t sizeA8 = fontRenderer->getFontRendererSize(i, GL_ALPHA);
- const uint32_t sizeRGBA = fontRenderer->getFontRendererSize(i, GL_RGBA);
- log.appendFormat(" FontRenderer %d A8 %8d / %8d\n", i, sizeA8, sizeA8);
- log.appendFormat(" FontRenderer %d RGBA %8d / %8d\n", i, sizeRGBA, sizeRGBA);
- log.appendFormat(" FontRenderer %d total %8d / %8d\n", i, sizeA8 + sizeRGBA,
- sizeA8 + sizeRGBA);
- }
+
+ const uint32_t sizeA8 = fontRenderer.getFontRendererSize(GL_ALPHA);
+ const uint32_t sizeRGBA = fontRenderer.getFontRendererSize(GL_RGBA);
+ log.appendFormat(" FontRenderer A8 %8d / %8d\n", sizeA8, sizeA8);
+ log.appendFormat(" FontRenderer RGBA %8d / %8d\n", sizeRGBA, sizeRGBA);
+ log.appendFormat(" FontRenderer total %8d / %8d\n", sizeA8 + sizeRGBA,
+ sizeA8 + sizeRGBA);
+
log.appendFormat("Other:\n");
log.appendFormat(" FboCache %8d / %8d\n",
fboCache.getSize(), fboCache.getMaxSize());
@@ -222,10 +211,8 @@
total += tessellationCache.getSize();
total += dropShadowCache.getSize();
total += patchCache.getSize();
- for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
- total += fontRenderer->getFontRendererSize(i, GL_ALPHA);
- total += fontRenderer->getFontRendererSize(i, GL_RGBA);
- }
+ total += fontRenderer.getFontRendererSize(GL_ALPHA);
+ total += fontRenderer.getFontRendererSize(GL_RGBA);
log.appendFormat("Total memory usage:\n");
log.appendFormat(" %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
@@ -250,12 +237,12 @@
patchCache.clear();
dropShadowCache.clear();
gradientCache.clear();
- fontRenderer->clear();
+ fontRenderer.clear();
fboCache.clear();
dither.clear();
// fall through
case FlushMode::Moderate:
- fontRenderer->flush();
+ fontRenderer.flush();
textureCache.flush();
pathCache.clear();
tessellationCache.clear();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 929db17..61e958d 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -21,6 +21,7 @@
#include "Dither.h"
#include "Extensions.h"
#include "FboCache.h"
+#include "GammaFontRenderer.h"
#include "GradientCache.h"
#include "LayerCache.h"
#include "PatchCache.h"
@@ -53,8 +54,6 @@
namespace android {
namespace uirenderer {
-class GammaFontRenderer;
-
///////////////////////////////////////////////////////////////////////////////
// Caches
///////////////////////////////////////////////////////////////////////////////
@@ -156,7 +155,7 @@
TextDropShadowCache dropShadowCache;
FboCache fboCache;
- GammaFontRenderer* fontRenderer;
+ GammaFontRenderer fontRenderer;
TaskManager tasks;
@@ -178,8 +177,6 @@
TextureState& textureState() { return *mTextureState; }
private:
-
- void initFont();
void initExtensions();
void initConstraints();
void initStaticProperties();
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 5808aac..e98fa04 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -20,9 +20,6 @@
// Turn on to check for OpenGL errors on each frame
#define DEBUG_OPENGL 1
-// Turn on to display informations about the GPU
-#define DEBUG_EXTENSIONS 0
-
// Turn on to enable initialization information
#define DEBUG_INIT 0
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 9fb1e75..a81ffb9 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -631,7 +631,7 @@
void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
ATRACE_NAME("flush drawing commands");
- Caches::getInstance().fontRenderer->endPrecaching();
+ Caches::getInstance().fontRenderer.endPrecaching();
if (isEmpty()) return; // nothing to flush
renderer.restoreToCount(1);
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 14126a9..dc5cb8b 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1246,7 +1246,7 @@
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
const DeferredDisplayState& state) override {
- FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint);
+ FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer();
fontRenderer.precache(mPaint, mText, mCount, SkMatrix::I());
deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
@@ -1311,7 +1311,7 @@
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
const DeferredDisplayState& state) override {
- FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint);
+ FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer();
SkMatrix transform;
renderer.findBestFontTransform(state.mMatrix, &transform);
if (mPrecacheTransform != transform) {
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 3d350c9..06c8a21 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -18,23 +18,14 @@
#include "Debug.h"
#include "Properties.h"
+#include "utils/StringUtils.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
#include <GLES2/gl2ext.h>
#include <utils/Log.h>
namespace android {
-
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(Extensions);
-
namespace uirenderer {
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
// Debug
#if DEBUG_EXTENSIONS
#define EXT_LOGD(...) ALOGD(__VA_ARGS__)
@@ -42,20 +33,16 @@
#define EXT_LOGD(...)
#endif
-///////////////////////////////////////////////////////////////////////////////
-// Constructors
-///////////////////////////////////////////////////////////////////////////////
Extensions::Extensions() {
- // Query GL extensions
- findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList);
- mHasNPot = hasGlExtension("GL_OES_texture_npot");
- mHasFramebufferFetch = hasGlExtension("GL_NV_shader_framebuffer_fetch");
- mHasDiscardFramebuffer = hasGlExtension("GL_EXT_discard_framebuffer");
- mHasDebugMarker = hasGlExtension("GL_EXT_debug_marker");
- mHas1BitStencil = hasGlExtension("GL_OES_stencil1");
- mHas4BitStencil = hasGlExtension("GL_OES_stencil4");
- mHasUnpackSubImage = hasGlExtension("GL_EXT_unpack_subimage");
+ StringCollection extensions((const char*) glGetString(GL_EXTENSIONS));
+ mHasNPot = extensions.has("GL_OES_texture_npot");
+ mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch");
+ mHasDiscardFramebuffer = extensions.has("GL_EXT_discard_framebuffer");
+ mHasDebugMarker = extensions.has("GL_EXT_debug_marker");
+ mHas1BitStencil = extensions.has("GL_OES_stencil1");
+ mHas4BitStencil = extensions.has("GL_OES_stencil4");
+ mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
const char* version = (const char*) glGetString(GL_VERSION);
@@ -78,40 +65,5 @@
}
}
-///////////////////////////////////////////////////////////////////////////////
-// Methods
-///////////////////////////////////////////////////////////////////////////////
-
-bool Extensions::hasGlExtension(const char* extension) const {
- const String8 s(extension);
- return mGlExtensionList.indexOf(s) >= 0;
-}
-
-bool Extensions::hasEglExtension(const char* extension) const {
- const String8 s(extension);
- return mEglExtensionList.indexOf(s) >= 0;
-}
-
-void Extensions::findExtensions(const char* extensions, SortedVector<String8>& list) const {
- const char* current = extensions;
- const char* head = current;
- EXT_LOGD("Available extensions:");
- do {
- head = strchr(current, ' ');
- String8 s(current, head ? head - current : strlen(current));
- if (s.length()) {
- list.add(s);
- EXT_LOGD(" %s", s.string());
- }
- current = head + 1;
- } while (head);
-}
-
-void Extensions::dump() const {
- ALOGD("%s", (const char*) glGetString(GL_VERSION));
- ALOGD("Supported GL extensions:\n%s", (const char*) glGetString(GL_EXTENSIONS));
- ALOGD("Supported EGL extensions:\n%s", eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS));
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 636b631..0a30d16 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -50,17 +50,7 @@
inline int getMajorGlVersion() const { return mVersionMajor; }
inline int getMinorGlVersion() const { return mVersionMinor; }
- bool hasGlExtension(const char* extension) const;
- bool hasEglExtension(const char* extension) const;
-
- void dump() const;
-
private:
- void findExtensions(const char* extensions, SortedVector<String8>& list) const;
-
- SortedVector<String8> mGlExtensionList;
- SortedVector<String8> mEglExtensionList;
-
bool mHasNPot;
bool mHasFramebufferFetch;
bool mHasDiscardFramebuffer;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 75c3ead..4b9d4f9 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -75,8 +75,8 @@
static bool sLogFontRendererCreate = true;
-FontRenderer::FontRenderer()
- : mGammaTable(nullptr)
+FontRenderer::FontRenderer(const uint8_t* gammaTable)
+ : mGammaTable(gammaTable)
, mCurrentFont(nullptr)
, mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity)
, mCurrentCacheTexture(nullptr)
@@ -92,27 +92,15 @@
INIT_LOGD("Creating FontRenderer");
}
- mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
- mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
- mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
- mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
+ mSmallCacheWidth = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_WIDTH,
+ DEFAULT_TEXT_SMALL_CACHE_WIDTH);
+ mSmallCacheHeight = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_HEIGHT,
+ DEFAULT_TEXT_SMALL_CACHE_HEIGHT);
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, nullptr) > 0) {
- mSmallCacheWidth = atoi(property);
- }
-
- if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, nullptr) > 0) {
- mSmallCacheHeight = atoi(property);
- }
-
- if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, nullptr) > 0) {
- mLargeCacheWidth = atoi(property);
- }
-
- if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, nullptr) > 0) {
- mLargeCacheHeight = atoi(property);
- }
+ mLargeCacheWidth = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_WIDTH,
+ DEFAULT_TEXT_LARGE_CACHE_WIDTH);
+ mLargeCacheHeight = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_HEIGHT,
+ DEFAULT_TEXT_LARGE_CACHE_HEIGHT);
uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 2954975..936c838 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -72,16 +72,12 @@
class FontRenderer {
public:
- FontRenderer();
+ FontRenderer(const uint8_t* gammaTable);
~FontRenderer();
void flushLargeCaches(std::vector<CacheTexture*>& cacheTextures);
void flushLargeCaches();
- void setGammaTable(const uint8_t* gammaTable) {
- mGammaTable = gammaTable;
- }
-
void setFont(const SkPaint* paint, const SkMatrix& matrix);
void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix);
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index 0bcd83a..96cac86 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -21,231 +21,22 @@
namespace android {
namespace uirenderer {
-///////////////////////////////////////////////////////////////////////////////
-// Utils
-///////////////////////////////////////////////////////////////////////////////
-
-static int luminance(const SkPaint* paint) {
- uint32_t c = paint->getColor();
- const int r = (c >> 16) & 0xFF;
- const int g = (c >> 8) & 0xFF;
- const int b = (c ) & 0xFF;
- return (r * 2 + g * 5 + b) >> 3;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Base class GammaFontRenderer
-///////////////////////////////////////////////////////////////////////////////
-
-GammaFontRenderer* GammaFontRenderer::createRenderer() {
- // Choose the best renderer
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_TEXT_GAMMA_METHOD, property, DEFAULT_TEXT_GAMMA_METHOD) > 0) {
- if (!strcasecmp(property, "lookup")) {
- return new LookupGammaFontRenderer();
- } else if (!strcasecmp(property, "shader")) {
- return new ShaderGammaFontRenderer(false);
- } else if (!strcasecmp(property, "shader3")) {
- return new ShaderGammaFontRenderer(true);
- }
- }
-
- return new Lookup3GammaFontRenderer();
-}
-
GammaFontRenderer::GammaFontRenderer() {
- // Get the renderer properties
- char property[PROPERTY_VALUE_MAX];
-
- // Get the gamma
- mGamma = DEFAULT_TEXT_GAMMA;
- if (property_get(PROPERTY_TEXT_GAMMA, property, nullptr) > 0) {
- INIT_LOGD(" Setting text gamma to %s", property);
- mGamma = atof(property);
- } else {
- INIT_LOGD(" Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
- }
-
- // Get the black gamma threshold
- mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
- if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, nullptr) > 0) {
- INIT_LOGD(" Setting text black gamma threshold to %s", property);
- mBlackThreshold = atoi(property);
- } else {
- INIT_LOGD(" Using default text black gamma threshold of %d",
- DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
- }
-
- // Get the white gamma threshold
- mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
- if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, nullptr) > 0) {
- INIT_LOGD(" Setting text white gamma threshold to %s", property);
- mWhiteThreshold = atoi(property);
- } else {
- INIT_LOGD(" Using default white black gamma threshold of %d",
- DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
- }
-}
-
-GammaFontRenderer::~GammaFontRenderer() {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Shader-based renderer
-///////////////////////////////////////////////////////////////////////////////
-
-ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma)
- : GammaFontRenderer() {
- INIT_LOGD("Creating shader gamma font renderer");
- mRenderer = nullptr;
- mMultiGamma = multiGamma;
-}
-
-void ShaderGammaFontRenderer::describe(ProgramDescription& description,
- const SkPaint* paint) const {
- if (paint->getShader() == nullptr) {
- if (mMultiGamma) {
- const int l = luminance(paint);
-
- if (l <= mBlackThreshold) {
- description.hasGammaCorrection = true;
- description.gamma = mGamma;
- } else if (l >= mWhiteThreshold) {
- description.hasGammaCorrection = true;
- description.gamma = 1.0f / mGamma;
- }
- } else {
- description.hasGammaCorrection = true;
- description.gamma = 1.0f / mGamma;
- }
- }
-}
-
-void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
- Program& program) const {
- if (description.hasGammaCorrection) {
- glUniform1f(program.getUniform("gamma"), description.gamma);
- }
-}
-
-void ShaderGammaFontRenderer::endPrecaching() {
- if (mRenderer) {
- mRenderer->endPrecaching();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Lookup-based renderer
-///////////////////////////////////////////////////////////////////////////////
-
-LookupGammaFontRenderer::LookupGammaFontRenderer()
- : GammaFontRenderer() {
INIT_LOGD("Creating lookup gamma font renderer");
// Compute the gamma tables
- const float gamma = 1.0f / mGamma;
+ const float gamma = 1.0f / Properties::textGamma;
for (uint32_t i = 0; i <= 255; i++) {
mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f));
}
-
- mRenderer = nullptr;
}
-void LookupGammaFontRenderer::endPrecaching() {
+void GammaFontRenderer::endPrecaching() {
if (mRenderer) {
mRenderer->endPrecaching();
}
}
-///////////////////////////////////////////////////////////////////////////////
-// Lookup-based renderer, using 3 different correction tables
-///////////////////////////////////////////////////////////////////////////////
-
-Lookup3GammaFontRenderer::Lookup3GammaFontRenderer()
- : GammaFontRenderer() {
- INIT_LOGD("Creating lookup3 gamma font renderer");
-
- // Compute the gamma tables
- const float blackGamma = mGamma;
- const float whiteGamma = 1.0f / mGamma;
-
- for (uint32_t i = 0; i <= 255; i++) {
- const float v = i / 255.0f;
- const float black = pow(v, blackGamma);
- const float white = pow(v, whiteGamma);
-
- mGammaTable[i] = i;
- mGammaTable[256 + i] = uint8_t((float)::floor(black * 255.0f + 0.5f));
- mGammaTable[512 + i] = uint8_t((float)::floor(white * 255.0f + 0.5f));
- }
-
- memset(mRenderers, 0, sizeof(FontRenderer*) * kGammaCount);
- memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount);
-}
-
-void Lookup3GammaFontRenderer::endPrecaching() {
- for (int i = 0; i < kGammaCount; i++) {
- if (mRenderers[i]) {
- mRenderers[i]->endPrecaching();
- }
- }
-}
-
-void Lookup3GammaFontRenderer::clear() {
- for (int i = 0; i < kGammaCount; i++) {
- mRenderers[i].release();
- }
-}
-
-void Lookup3GammaFontRenderer::flush() {
- int count = 0;
- int min = -1;
- uint32_t minCount = UINT_MAX;
-
- for (int i = 0; i < kGammaCount; i++) {
- if (mRenderers[i]) {
- count++;
- if (mRenderersUsageCount[i] < minCount) {
- minCount = mRenderersUsageCount[i];
- min = i;
- }
- }
- }
-
- if (count <= 1 || min < 0) return;
-
- mRenderers[min].release();
-
- // Also eliminate the caches for large glyphs, as they consume significant memory
- for (int i = 0; i < kGammaCount; ++i) {
- if (mRenderers[i]) {
- mRenderers[i]->flushLargeCaches();
- }
- }
-}
-
-FontRenderer* Lookup3GammaFontRenderer::getRenderer(Gamma gamma) {
- if (!mRenderers[gamma]) {
- mRenderers[gamma].reset(new FontRenderer());
- mRenderers[gamma]->setGammaTable(&mGammaTable[gamma * 256]);
- }
- mRenderersUsageCount[gamma]++;
- return mRenderers[gamma].get();
-}
-
-FontRenderer& Lookup3GammaFontRenderer::getFontRenderer(const SkPaint* paint) {
- if (paint->getShader() == nullptr) {
- const int l = luminance(paint);
-
- if (l <= mBlackThreshold) {
- return *getRenderer(kGammaBlack);
- } else if (l >= mWhiteThreshold) {
- return *getRenderer(kGammaWhite);
- }
- }
- return *getRenderer(kGammaDefault);
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index ca55bf1..146d385 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -17,183 +17,44 @@
#ifndef ANDROID_HWUI_GAMMA_FONT_RENDERER_H
#define ANDROID_HWUI_GAMMA_FONT_RENDERER_H
-#include <SkPaint.h>
-
#include "FontRenderer.h"
#include "Program.h"
+#include <SkPaint.h>
+
namespace android {
namespace uirenderer {
class GammaFontRenderer {
public:
- virtual ~GammaFontRenderer();
-
- virtual void clear() = 0;
- virtual void flush() = 0;
-
- virtual FontRenderer& getFontRenderer(const SkPaint* paint) = 0;
-
- virtual uint32_t getFontRendererCount() const = 0;
- virtual uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const = 0;
-
- virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
- virtual void setupProgram(ProgramDescription& description, Program& program) const = 0;
-
- virtual void endPrecaching() = 0;
-
- static GammaFontRenderer* createRenderer();
-
-protected:
GammaFontRenderer();
- int mBlackThreshold;
- int mWhiteThreshold;
-
- float mGamma;
-};
-
-class ShaderGammaFontRenderer: public GammaFontRenderer {
-public:
- ~ShaderGammaFontRenderer() {
- delete mRenderer;
+ void clear() {
+ mRenderer.release();
}
- void clear() override {
- delete mRenderer;
- mRenderer = nullptr;
- }
-
- void flush() override {
+ void flush() {
if (mRenderer) {
mRenderer->flushLargeCaches();
}
}
- FontRenderer& getFontRenderer(const SkPaint* paint) override {
+ FontRenderer& getFontRenderer() {
if (!mRenderer) {
- mRenderer = new FontRenderer;
+ mRenderer.reset(new FontRenderer(&mGammaTable[0]));
}
return *mRenderer;
}
- uint32_t getFontRendererCount() const override {
- return 1;
- }
-
- uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
+ uint32_t getFontRendererSize(GLenum format) const {
return mRenderer ? mRenderer->getCacheSize(format) : 0;
}
- void describe(ProgramDescription& description, const SkPaint* paint) const override;
- void setupProgram(ProgramDescription& description, Program& program) const override;
-
- void endPrecaching() override;
+ void endPrecaching();
private:
- ShaderGammaFontRenderer(bool multiGamma);
-
- FontRenderer* mRenderer;
- bool mMultiGamma;
-
- friend class GammaFontRenderer;
-};
-
-class LookupGammaFontRenderer: public GammaFontRenderer {
-public:
- ~LookupGammaFontRenderer() {
- delete mRenderer;
- }
-
- void clear() override {
- delete mRenderer;
- mRenderer = nullptr;
- }
-
- void flush() override {
- if (mRenderer) {
- mRenderer->flushLargeCaches();
- }
- }
-
- FontRenderer& getFontRenderer(const SkPaint* paint) override {
- if (!mRenderer) {
- mRenderer = new FontRenderer;
- mRenderer->setGammaTable(&mGammaTable[0]);
- }
- return *mRenderer;
- }
-
- uint32_t getFontRendererCount() const override {
- return 1;
- }
-
- uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
- return mRenderer ? mRenderer->getCacheSize(format) : 0;
- }
-
- void describe(ProgramDescription& description, const SkPaint* paint) const override {
- }
-
- void setupProgram(ProgramDescription& description, Program& program) const override {
- }
-
- void endPrecaching() override;
-
-private:
- LookupGammaFontRenderer();
-
- FontRenderer* mRenderer;
+ std::unique_ptr<FontRenderer> mRenderer;
uint8_t mGammaTable[256];
-
- friend class GammaFontRenderer;
-};
-
-class Lookup3GammaFontRenderer: public GammaFontRenderer {
-public:
- void clear() override;
- void flush() override;
-
- FontRenderer& getFontRenderer(const SkPaint* paint) override;
-
- uint32_t getFontRendererCount() const override {
- return kGammaCount;
- }
-
- uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
- if (fontRenderer >= kGammaCount) return 0;
-
- if (!mRenderers[fontRenderer]) return 0;
-
- return mRenderers[fontRenderer]->getCacheSize(format);
- }
-
- void describe(ProgramDescription& description, const SkPaint* paint) const override {
- }
-
- void setupProgram(ProgramDescription& description, Program& program) const override {
- }
-
- void endPrecaching() override;
-
-private:
- Lookup3GammaFontRenderer();
-
- enum Gamma {
- kGammaDefault = 0,
- kGammaBlack = 1,
- kGammaWhite = 2,
- kGammaCount = 3
- };
-
- FontRenderer* getRenderer(Gamma gamma);
-
- uint32_t mRenderersUsageCount[kGammaCount];
- std::unique_ptr<FontRenderer> mRenderers[kGammaCount];
-
- uint8_t mGammaTable[256 * kGammaCount];
-
- friend class GammaFontRenderer;
};
}; // namespace uirenderer
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index e27b26b..69559a7 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -435,7 +435,6 @@
mOutGlop->fill.texture = { &texture,
GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
- mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter);
@@ -449,7 +448,6 @@
mOutGlop->fill.texture = { &(layer.getTexture()),
layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
- mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap,
nullptr, layer.getColorFilter());
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e06e348..a401ce1 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2014,7 +2014,7 @@
y = floorf(y + currentTransform()->getTranslateY() + 0.5f);
}
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
fontRenderer.setFont(paint, SkMatrix::I());
int alpha;
@@ -2166,7 +2166,7 @@
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
if (CC_UNLIKELY(hasTextShadow(paint))) {
fontRenderer.setFont(paint, SkMatrix::I());
@@ -2234,7 +2234,7 @@
// TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
mRenderState.scissor().setEnabled(true);
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
fontRenderer.setFont(paint, SkMatrix::I());
fontRenderer.setTextureFiltering(true);
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index f503e5d..4031f2e1 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -138,10 +138,10 @@
mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_PATH_CACHE_SIZE, property, nullptr) > 0) {
- INIT_LOGD(" Setting %s cache size to %sMB", name, property);
+ INIT_LOGD(" Setting path cache size to %sMB", property);
mMaxSize = MB(atof(property));
} else {
- INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
+ INIT_LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
}
mCache.setOnEntryRemovedListener(this);
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index b09c207..e5200a5 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -78,14 +78,12 @@
#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
-#define PROGRAM_HAS_GAMMA_CORRECTION 40
+#define PROGRAM_IS_SIMPLE_GRADIENT 40
-#define PROGRAM_IS_SIMPLE_GRADIENT 41
+#define PROGRAM_HAS_COLORS 41
-#define PROGRAM_HAS_COLORS 42
-
-#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
-#define PROGRAM_HAS_ROUND_RECT_CLIP 44
+#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
+#define PROGRAM_HAS_ROUND_RECT_CLIP 43
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -157,9 +155,6 @@
SkXfermode::Mode framebufferMode;
bool swapSrcDst;
- bool hasGammaCorrection;
- float gamma;
-
bool hasDebugHighlight;
bool hasRoundRectClip;
@@ -199,9 +194,6 @@
framebufferMode = SkXfermode::kClear_Mode;
swapSrcDst = false;
- hasGammaCorrection = false;
- gamma = 2.2f;
-
hasDebugHighlight = false;
hasRoundRectClip = false;
}
@@ -266,7 +258,6 @@
if (useShadowAlphaInterp) key |= programid(0x1) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
- if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index b25a4ac..05be488 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -167,8 +167,6 @@
// PorterDuff
"uniform vec4 colorBlend;\n"
};
-const char* gFS_Uniforms_Gamma =
- "uniform float gamma;\n";
const char* gFS_Uniforms_HasRoundRectClip =
"uniform vec4 roundRectInnerRectLTRB;\n"
@@ -204,18 +202,10 @@
"\nvoid main(void) {\n"
" gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
"}\n\n";
-const char* gFS_Fast_SingleA8Texture_ApplyGamma =
- "\nvoid main(void) {\n"
- " gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, gamma));\n"
- "}\n\n";
const char* gFS_Fast_SingleModulateA8Texture =
"\nvoid main(void) {\n"
" gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n"
"}\n\n";
-const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
- "\nvoid main(void) {\n"
- " gl_FragColor = color * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
- "}\n\n";
const char* gFS_Fast_SingleGradient[2] = {
"\nvoid main(void) {\n"
" gl_FragColor = %s + texture2D(gradientSampler, linear);\n"
@@ -250,13 +240,11 @@
// Modulate
" fragColor = color * texture2D(baseSampler, outTexCoords);\n"
};
-const char* gFS_Main_FetchA8Texture[4] = {
+const char* gFS_Main_FetchA8Texture[2] = {
// Don't modulate
" fragColor = texture2D(baseSampler, outTexCoords);\n",
- " fragColor = texture2D(baseSampler, outTexCoords);\n",
// Modulate
" fragColor = color * texture2D(baseSampler, outTexCoords).a;\n",
- " fragColor = color * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
const char* gFS_Main_FetchGradient[6] = {
// Linear
@@ -284,38 +272,29 @@
" fragColor = blendShaders(gradientColor, bitmapColor)";
const char* gFS_Main_BlendShadersGB =
" fragColor = blendShaders(bitmapColor, gradientColor)";
-const char* gFS_Main_BlendShaders_Modulate[6] = {
+const char* gFS_Main_BlendShaders_Modulate[3] = {
// Don't modulate
";\n",
- ";\n",
// Modulate
" * color.a;\n",
- " * color.a;\n",
// Modulate with alpha 8 texture
" * texture2D(baseSampler, outTexCoords).a;\n",
- " * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
-const char* gFS_Main_GradientShader_Modulate[6] = {
+const char* gFS_Main_GradientShader_Modulate[3] = {
// Don't modulate
" fragColor = gradientColor;\n",
- " fragColor = gradientColor;\n",
// Modulate
" fragColor = gradientColor * color.a;\n",
- " fragColor = gradientColor * color.a;\n",
// Modulate with alpha 8 texture
" fragColor = gradientColor * texture2D(baseSampler, outTexCoords).a;\n",
- " fragColor = gradientColor * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
-const char* gFS_Main_BitmapShader_Modulate[6] = {
+const char* gFS_Main_BitmapShader_Modulate[3] = {
// Don't modulate
" fragColor = bitmapColor;\n",
- " fragColor = bitmapColor;\n",
// Modulate
" fragColor = bitmapColor * color.a;\n",
- " fragColor = bitmapColor * color.a;\n",
// Modulate with alpha 8 texture
" fragColor = bitmapColor * texture2D(baseSampler, outTexCoords).a;\n",
- " fragColor = bitmapColor * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
const char* gFS_Main_FragColor =
" gl_FragColor = fragColor;\n";
@@ -540,7 +519,6 @@
static bool shaderOp(const ProgramDescription& description, String8& shader,
const int modulateOp, const char** snippets) {
int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
- op = op * 2 + description.hasGammaCorrection;
shader.append(snippets[op]);
return description.hasAlpha8Texture;
}
@@ -596,9 +574,6 @@
shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
gFS_Uniforms_Dither);
}
- if (description.hasGammaCorrection) {
- shader.append(gFS_Uniforms_Gamma);
- }
if (description.hasRoundRectClip) {
shader.append(gFS_Uniforms_HasRoundRectClip);
}
@@ -633,17 +608,9 @@
fast = true;
} else if (singleA8Texture) {
if (!description.modulate) {
- if (description.hasGammaCorrection) {
- shader.append(gFS_Fast_SingleA8Texture_ApplyGamma);
- } else {
- shader.append(gFS_Fast_SingleA8Texture);
- }
+ shader.append(gFS_Fast_SingleA8Texture);
} else {
- if (description.hasGammaCorrection) {
- shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma);
- } else {
- shader.append(gFS_Fast_SingleModulateA8Texture);
- }
+ shader.append(gFS_Fast_SingleModulateA8Texture);
}
fast = true;
} else if (singleGradient) {
@@ -693,8 +660,7 @@
if (description.hasTexture || description.hasExternalTexture) {
if (description.hasAlpha8Texture) {
if (!description.hasGradient && !description.hasBitmap) {
- shader.append(gFS_Main_FetchA8Texture[modulateOp * 2 +
- description.hasGammaCorrection]);
+ shader.append(gFS_Main_FetchA8Texture[modulateOp]);
}
} else {
shader.append(gFS_Main_FetchTexture[modulateOp]);
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index b8f8585..36a8dac 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -33,6 +33,8 @@
bool Properties::useBufferAge = true;
bool Properties::enablePartialUpdates = true;
+float Properties::textGamma = DEFAULT_TEXT_GAMMA;
+
DebugLevel Properties::debugLevel = kDebugDisabled;
OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
StencilClipDebug Properties::debugStencilClip = StencilClipDebug::Hide;
@@ -47,6 +49,15 @@
ProfileType Properties::sProfileType = ProfileType::None;
bool Properties::sDisableProfileBars = false;
+static float property_get_float(const char* key, float defaultValue) {
+ char buf[PROPERTY_VALUE_MAX] = {'\0',};
+
+ if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
+ return atof(buf);
+ }
+ return defaultValue;
+}
+
bool Properties::load() {
char property[PROPERTY_VALUE_MAX];
bool prevDebugLayersUpdates = debugLayersUpdates;
@@ -110,6 +121,8 @@
useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true);
enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true);
+ textGamma = property_get_float(PROPERTY_TEXT_GAMMA, DEFAULT_TEXT_GAMMA);
+
return (prevDebugLayersUpdates != debugLayersUpdates)
|| (prevDebugOverdraw != debugOverdraw)
|| (prevDebugStencilClip != debugStencilClip);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7602848..3512c36 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -208,30 +208,8 @@
#define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width"
#define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height"
-// Indicates whether gamma correction should be applied in the shaders
-// or in lookup tables. Accepted values:
-//
-// - "lookup3", correction based on lookup tables. Gamma correction
-// is different for black and white text (see thresholds below)
-//
-// - "lookup", correction based on a single lookup table
-//
-// - "shader3", correction applied by a GLSL shader. Gamma correction
-// is different for black and white text (see thresholds below)
-//
-// - "shader", correction applied by a GLSL shader
-//
-// See PROPERTY_TEXT_GAMMA, PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD and
-// PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD for more control.
-#define PROPERTY_TEXT_GAMMA_METHOD "hwui.text_gamma_correction"
-#define DEFAULT_TEXT_GAMMA_METHOD "lookup"
-
// Gamma (>= 1.0, <= 10.0)
#define PROPERTY_TEXT_GAMMA "hwui.text_gamma"
-// Luminance threshold below which black gamma correction is applied. Range: [0..255]
-#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "hwui.text_gamma.black_threshold"
-// Lumincance threshold above which white gamma correction is applied. Range: [0..255]
-#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "hwui.text_gamma.white_threshold"
///////////////////////////////////////////////////////////////////////////////
// Default property values
@@ -242,7 +220,7 @@
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
#define DEFAULT_PATH_CACHE_SIZE 4.0f
#define DEFAULT_VERTEX_CACHE_SIZE 1.0f
-#define DEFAULT_PATCH_CACHE_SIZE 128 // in kB
+#define DEFAULT_PATCH_CACHE_SIZE 128.0f // in kB
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
#define DEFAULT_FBO_CACHE_SIZE 0
@@ -250,8 +228,6 @@
#define DEFAULT_TEXTURE_CACHE_FLUSH_RATE 0.6f
#define DEFAULT_TEXT_GAMMA 1.4f
-#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
-#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
///////////////////////////////////////////////////////////////////////////////
// Misc
@@ -300,6 +276,8 @@
static bool useBufferAge;
static bool enablePartialUpdates;
+ static float textGamma;
+
static DebugLevel debugLevel;
static OverdrawColorSet overdrawColorSet;
static StencilClipDebug debugStencilClip;
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 01f5e2d..12a3e76 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -312,10 +312,10 @@
, mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, nullptr) > 0) {
- INIT_LOGD(" Setting %s cache size to %sMB", name, property);
+ INIT_LOGD(" Setting tessellation cache size to %sMB", property);
setMaxSize(MB(atof(property)));
} else {
- INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_VERTEX_CACHE_SIZE);
+ INIT_LOGD(" Using default tessellation cache size of %.2fMB", DEFAULT_VERTEX_CACHE_SIZE);
}
mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 16f6f4b..a6c72a3 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -16,9 +16,6 @@
#include <GLES2/gl2.h>
-#include <SkCanvas.h>
-#include <SkPixelRef.h>
-
#include <utils/Mutex.h>
#include "AssetAtlas.h"
@@ -169,7 +166,7 @@
if (canCache) {
texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
- generateTexture(bitmap, texture, false);
+ Caches::getInstance().textureState().generateTexture(bitmap, texture, false);
mSize += size;
TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
@@ -182,7 +179,7 @@
} else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
// Texture was in the cache but is dirty, re-upload
// TODO: Re-adjust the cache size if the bitmap's dimensions have changed
- generateTexture(bitmap, texture, true);
+ Caches::getInstance().textureState().generateTexture(bitmap, texture, true);
}
return texture;
@@ -207,7 +204,7 @@
const uint32_t size = bitmap->rowBytes() * bitmap->height();
texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
- generateTexture(bitmap, texture, false);
+ Caches::getInstance().textureState().generateTexture(bitmap, texture, false);
texture->cleanup = true;
}
@@ -249,133 +246,5 @@
}
}
-void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) {
- SkAutoLockPixels alp(*bitmap);
-
- if (!bitmap->readyToDraw()) {
- ALOGE("Cannot generate texture from bitmap");
- return;
- }
-
- ATRACE_FORMAT("Upload %ux%u Texture", bitmap->width(), bitmap->height());
-
- // We could also enable mipmapping if both bitmap dimensions are powers
- // of 2 but we'd have to deal with size changes. Let's keep this simple
- const bool canMipMap = Caches::getInstance().extensions().hasNPot();
-
- // If the texture had mipmap enabled but not anymore,
- // force a glTexImage2D to discard the mipmap levels
- const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
- bitmap->height() != int(texture->height) ||
- (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap());
-
- if (!regenerate) {
- glGenTextures(1, &texture->id);
- }
-
- texture->generation = bitmap->getGenerationID();
- texture->width = bitmap->width();
- texture->height = bitmap->height();
-
- Caches::getInstance().textureState().bindTexture(texture->id);
-
- switch (bitmap->colorType()) {
- case kAlpha_8_SkColorType:
- uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
- texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
- texture->blend = true;
- break;
- case kRGB_565_SkColorType:
- uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
- texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
- texture->blend = false;
- break;
- case kN32_SkColorType:
- uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
- texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
- // Do this after calling getPixels() to make sure Skia's deferred
- // decoding happened
- texture->blend = !bitmap->isOpaque();
- break;
- case kARGB_4444_SkColorType:
- case kIndex_8_SkColorType:
- uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
- texture->blend = !bitmap->isOpaque();
- break;
- default:
- ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType());
- break;
- }
-
- if (canMipMap) {
- texture->mipMap = bitmap->hasHardwareMipMap();
- if (texture->mipMap) {
- glGenerateMipmap(GL_TEXTURE_2D);
- }
- }
-
- if (!regenerate) {
- texture->setFilter(GL_NEAREST);
- texture->setWrap(GL_CLAMP_TO_EDGE);
- }
-}
-
-void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
- uint32_t width, uint32_t height) {
- SkBitmap rgbaBitmap;
- rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType()));
- rgbaBitmap.eraseColor(0);
-
- SkCanvas canvas(rgbaBitmap);
- canvas.drawBitmap(*bitmap, 0.0f, 0.0f, nullptr);
-
- uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(),
- width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
-}
-
-void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
- GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
- glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
- const bool useStride = stride != width
- && Caches::getInstance().extensions().hasUnpackRowLength();
- if ((stride == width) || useStride) {
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
- }
-
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
- } else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
- }
-
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- }
- } else {
- // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
- // if the stride doesn't match the width
-
- GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
- if (!temp) return;
-
- uint8_t * pDst = (uint8_t *)temp;
- uint8_t * pSrc = (uint8_t *)data;
- for (GLsizei i = 0; i < height; i++) {
- memcpy(pDst, pSrc, width * bpp);
- pDst += width * bpp;
- pSrc += stride * bpp;
- }
-
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
- } else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
- }
-
- free(temp);
- }
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index ebd1885..191c8a8 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -144,18 +144,6 @@
Texture* get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType);
Texture* getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType);
- /**
- * Generates the texture from a bitmap into the specified texture structure.
- *
- * @param regenerate If true, the bitmap data is reuploaded into the texture, but
- * no new texture is generated.
- */
- void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false);
-
- void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height);
- void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
- GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
-
LruCache<uint32_t, Texture*> mCache;
uint32_t mSize;
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index ed853f7..98e6146 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -77,7 +77,7 @@
, canvasContext(clone.canvasContext)
{}
- const TraversalMode mode;
+ TraversalMode mode;
// TODO: Remove this? Currently this is used to signal to stop preparing
// textures if we run out of cache space.
bool prepareTextures;
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp
index b21e15e..93f787d 100644
--- a/libs/hwui/renderstate/Blend.cpp
+++ b/libs/hwui/renderstate/Blend.cpp
@@ -98,20 +98,6 @@
// gl blending off by default
}
-void Blend::enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage) {
- GLenum srcMode;
- GLenum dstMode;
- getFactors(mode, modeUsage, &srcMode, &dstMode);
- setFactors(srcMode, dstMode);
-}
-
-void Blend::disable() {
- if (mEnabled) {
- glDisable(GL_BLEND);
- mEnabled = false;
- }
-}
-
void Blend::invalidate() {
syncEnabled();
mSrcMode = mDstMode = GL_ZERO;
@@ -132,8 +118,13 @@
void Blend::setFactors(GLenum srcMode, GLenum dstMode) {
if (srcMode == GL_ZERO && dstMode == GL_ZERO) {
- disable();
+ // disable blending
+ if (mEnabled) {
+ glDisable(GL_BLEND);
+ mEnabled = false;
+ }
} else {
+ // enable blending
if (!mEnabled) {
glEnable(GL_BLEND);
mEnabled = true;
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
index dcc681d..df9e5a8 100644
--- a/libs/hwui/renderstate/Blend.h
+++ b/libs/hwui/renderstate/Blend.h
@@ -34,9 +34,6 @@
NoSwap,
Swap,
};
-
- void enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage);
- void disable();
void syncEnabled();
static void getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage,
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
index 987d4cd..1f50f71 100644
--- a/libs/hwui/renderstate/TextureState.cpp
+++ b/libs/hwui/renderstate/TextureState.cpp
@@ -15,6 +15,14 @@
*/
#include "renderstate/TextureState.h"
+#include "Caches.h"
+#include "utils/TraceUtils.h"
+
+#include <GLES3/gl3.h>
+#include <memory>
+#include <SkCanvas.h>
+#include <SkBitmap.h>
+
namespace android {
namespace uirenderer {
@@ -26,6 +34,134 @@
GL_TEXTURE3
};
+static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
+ GLsizei width, GLsizei height, const GLvoid * data) {
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
+ const bool useStride = stride != width
+ && Caches::getInstance().extensions().hasUnpackRowLength();
+ if ((stride == width) || useStride) {
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ }
+
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
+ }
+
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
+ } else {
+ // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+ // if the stride doesn't match the width
+
+ GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
+ if (!temp) return;
+
+ uint8_t * pDst = (uint8_t *)temp;
+ uint8_t * pSrc = (uint8_t *)data;
+ for (GLsizei i = 0; i < height; i++) {
+ memcpy(pDst, pSrc, width * bpp);
+ pDst += width * bpp;
+ pSrc += stride * bpp;
+ }
+
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
+ }
+
+ free(temp);
+ }
+}
+
+static void uploadSkBitmapToTexture(const SkBitmap& bitmap,
+ bool resize, GLenum format, GLenum type) {
+ uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(),
+ bitmap.width(), bitmap.height(), bitmap.getPixels());
+}
+
+void TextureState::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) {
+ SkAutoLockPixels alp(*bitmap);
+
+ if (!bitmap->readyToDraw()) {
+ ALOGE("Cannot generate texture from bitmap");
+ return;
+ }
+
+ ATRACE_FORMAT("Upload %ux%u Texture", bitmap->width(), bitmap->height());
+
+ // We could also enable mipmapping if both bitmap dimensions are powers
+ // of 2 but we'd have to deal with size changes. Let's keep this simple
+ const bool canMipMap = Caches::getInstance().extensions().hasNPot();
+
+ // If the texture had mipmap enabled but not anymore,
+ // force a glTexImage2D to discard the mipmap levels
+ const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
+ bitmap->height() != int(texture->height) ||
+ (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap());
+
+ if (!regenerate) {
+ glGenTextures(1, &texture->id);
+ }
+
+ texture->generation = bitmap->getGenerationID();
+ texture->width = bitmap->width();
+ texture->height = bitmap->height();
+
+ bindTexture(texture->id);
+
+ switch (bitmap->colorType()) {
+ case kAlpha_8_SkColorType:
+ uploadSkBitmapToTexture(*bitmap, resize, GL_ALPHA, GL_UNSIGNED_BYTE);
+ texture->blend = true;
+ break;
+ case kRGB_565_SkColorType:
+ uploadSkBitmapToTexture(*bitmap, resize, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
+ texture->blend = false;
+ break;
+ case kN32_SkColorType:
+ uploadSkBitmapToTexture(*bitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE);
+ // Do this after calling getPixels() to make sure Skia's deferred
+ // decoding happened
+ texture->blend = !bitmap->isOpaque();
+ break;
+ case kARGB_4444_SkColorType:
+ case kIndex_8_SkColorType: {
+ SkBitmap rgbaBitmap;
+ rgbaBitmap.allocPixels(SkImageInfo::MakeN32(texture->width, texture->height,
+ bitmap->alphaType()));
+ rgbaBitmap.eraseColor(0);
+
+ SkCanvas canvas(rgbaBitmap);
+ canvas.drawBitmap(*bitmap, 0.0f, 0.0f, nullptr);
+
+ uploadSkBitmapToTexture(rgbaBitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE);
+ texture->blend = !bitmap->isOpaque();
+ break;
+ }
+ default:
+ ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType());
+ break;
+ }
+
+ if (canMipMap) {
+ texture->mipMap = bitmap->hasHardwareMipMap();
+ if (texture->mipMap) {
+ glGenerateMipmap(GL_TEXTURE_2D);
+ }
+ }
+
+ if (!regenerate) {
+ texture->setFilter(GL_NEAREST);
+ texture->setWrap(GL_CLAMP_TO_EDGE);
+ }
+}
+
TextureState::TextureState()
: mTextureUnit(0) {
glActiveTexture(kTextureUnits[0]);
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
index d3c014c..3a2b85a 100644
--- a/libs/hwui/renderstate/TextureState.h
+++ b/libs/hwui/renderstate/TextureState.h
@@ -23,9 +23,13 @@
#include <SkXfermode.h>
#include <memory>
+class SkBitmap;
+
namespace android {
namespace uirenderer {
+class Texture;
+
class TextureState {
friend class Caches; // TODO: move to RenderState
public:
@@ -71,6 +75,14 @@
* Clear the cache of bound textures.
*/
void unbindTexture(GLuint texture);
+
+ /**
+ * Generates the texture from a bitmap into the specified texture structure.
+ *
+ * @param regenerate If true, the bitmap data is reuploaded into the texture, but
+ * no new texture is generated.
+ */
+ void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate);
private:
// total number of texture units available for use
static const int kTextureUnitsCount = 4;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b74b508..9dc5b45 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -60,9 +60,10 @@
, mEglManager(thread.eglManager())
, mOpaque(!translucent)
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
- , mRootRenderNode(rootRenderNode)
, mJankTracker(thread.timeLord().frameIntervalNanos())
- , mProfiler(mFrames) {
+ , mProfiler(mFrames)
+ , mContentOverdrawProtectionBounds(0, 0, 0, 0) {
+ mRenderNodes.emplace_back(rootRenderNode);
mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}
@@ -172,7 +173,8 @@
return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
}
-void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued) {
+void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
+ int64_t syncQueued, RenderNode* target) {
mRenderThread.removeFrameCallback(this);
// If the previous frame was dropped we don't need to hold onto it, so
@@ -189,7 +191,13 @@
info.canvasContext = this;
mAnimationContext->startFrame(info.mode);
- mRootRenderNode->prepareTree(info);
+ for (const sp<RenderNode>& node : mRenderNodes) {
+ // Only the primary target node will be drawn full - all other nodes would get drawn in
+ // real time mode. In case of a window, the primary node is the window content and the other
+ // node(s) are non client / filler nodes.
+ info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
+ node->prepareTree(info);
+ }
mAnimationContext->runRemainingAnimations(info);
freePrefetechedLayers();
@@ -299,7 +307,95 @@
dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque);
Rect outBounds;
- mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
+ // It there are multiple render nodes, they are as follows:
+ // #0 - backdrop
+ // #1 - content (with - and clipped to - bounds mContentOverdrawProtectionBounds)
+ // #2 - frame
+ // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
+ // resizing however it might become partially visible. The following render loop will crop the
+ // backdrop against the content and draw the remaining part of it. It will then crop the content
+ // against the backdrop (since that indicates a shrinking of the window) and then the frame
+ // around everything.
+ // The bounds of the backdrop against which the content should be clipped.
+ Rect backdropBounds = mContentOverdrawProtectionBounds;
+ // If there is no content bounds we ignore the layering as stated above and start with 2.
+ int layer = mContentOverdrawProtectionBounds.isEmpty() ? 2 : 0;
+ // Draw all render nodes. Note that
+ for (const sp<RenderNode>& node : mRenderNodes) {
+ if (layer == 0) { // Backdrop.
+ // Draw the backdrop clipped to the inverse content bounds.
+ const RenderProperties& properties = node->properties();
+ Rect targetBounds(properties.getLeft(), properties.getTop(),
+ properties.getRight(), properties.getBottom());
+ // Remember the intersection of the target bounds and the intersection bounds against
+ // which we have to crop the content.
+ backdropBounds.intersect(targetBounds);
+ // Check if we have to draw something on the left side ...
+ if (targetBounds.left < mContentOverdrawProtectionBounds.left) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag);
+ if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
+ mContentOverdrawProtectionBounds.left, targetBounds.bottom,
+ SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ // Reduce the target area by the area we have just painted.
+ targetBounds.left = std::min(mContentOverdrawProtectionBounds.left,
+ targetBounds.right);
+ mCanvas->restore();
+ }
+ // ... or on the right side ...
+ if (targetBounds.right > mContentOverdrawProtectionBounds.right &&
+ !targetBounds.isEmpty()) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag);
+ if (mCanvas->clipRect(mContentOverdrawProtectionBounds.right, targetBounds.top,
+ targetBounds.right, targetBounds.bottom,
+ SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ // Reduce the target area by the area we have just painted.
+ targetBounds.right = std::max(targetBounds.left,
+ mContentOverdrawProtectionBounds.right);
+ mCanvas->restore();
+ }
+ // ... or at the top ...
+ if (targetBounds.top < mContentOverdrawProtectionBounds.top &&
+ !targetBounds.isEmpty()) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag);
+ if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
+ mContentOverdrawProtectionBounds.top,
+ SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ // Reduce the target area by the area we have just painted.
+ targetBounds.top = std::min(mContentOverdrawProtectionBounds.top,
+ targetBounds.bottom);
+ mCanvas->restore();
+ }
+ // ... or at the bottom.
+ if (targetBounds.bottom > mContentOverdrawProtectionBounds.bottom &&
+ !targetBounds.isEmpty()) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag);
+ if (mCanvas->clipRect(targetBounds.left,
+ mContentOverdrawProtectionBounds.bottom, targetBounds.right,
+ targetBounds.bottom, SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ mCanvas->restore();
+ }
+ } else if (layer == 1) { // Content
+ // It gets cropped against the bounds of the backdrop to stay inside.
+ mCanvas->save(SkCanvas::kClip_SaveFlag);
+ if (mCanvas->clipRect(backdropBounds.left, backdropBounds.top,
+ backdropBounds.right, backdropBounds.bottom,
+ SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ mCanvas->restore();
+ } else { // draw the rest on top at will!
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ layer++;
+ }
profiler().draw(mCanvas);
@@ -343,7 +439,10 @@
if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
return;
}
+ prepareAndDraw(nullptr);
+}
+void CanvasContext::prepareAndDraw(RenderNode* node) {
ATRACE_CALL();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
@@ -353,7 +452,7 @@
mRenderThread.timeLord().latestVsync());
TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
- prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC));
+ prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node);
if (info.out.canDrawThisFrame) {
draw();
}
@@ -423,7 +522,9 @@
stopDrawing();
if (mEglManager.hasEglContext()) {
freePrefetechedLayers();
- mRootRenderNode->destroyHardwareResources();
+ for (const sp<RenderNode>& node : mRenderNodes) {
+ node->destroyHardwareResources();
+ }
Caches& caches = Caches::getInstance();
// Make sure to release all the textures we were owning as there won't
// be another draw
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6a79320..1c3845c 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -34,6 +34,7 @@
#include <set>
#include <string>
+#include <vector>
namespace android {
namespace uirenderer {
@@ -77,12 +78,14 @@
void setOpaque(bool opaque);
void makeCurrent();
void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
- void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued);
+ void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
+ int64_t syncQueued, RenderNode* target);
void draw();
void destroy();
// IFrameCallback, Chroreographer-driven frame callback entry point
virtual void doFrame() override;
+ void prepareAndDraw(RenderNode* node);
void buildLayer(RenderNode* node);
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
@@ -113,6 +116,20 @@
void serializeDisplayListTree();
+ void addRenderNode(RenderNode* node, bool placeFront) {
+ int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
+ mRenderNodes.emplace( mRenderNodes.begin() + pos, node);
+ }
+
+ void removeRenderNode(RenderNode* node) {
+ mRenderNodes.erase(std::remove(mRenderNodes.begin(), mRenderNodes.end(), node),
+ mRenderNodes.end());
+ }
+
+ void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+ mContentOverdrawProtectionBounds.set(left, top, right, bottom);
+ }
+
private:
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -138,7 +155,7 @@
DamageAccumulator mDamageAccumulator;
std::unique_ptr<AnimationContext> mAnimationContext;
- const sp<RenderNode> mRootRenderNode;
+ std::vector< sp<RenderNode> > mRenderNodes;
FrameInfo* mCurrentFrameInfo = nullptr;
// Ring buffer large enough for 2 seconds worth of frames
@@ -148,6 +165,9 @@
FrameInfoVisualizer mProfiler;
std::set<RenderNode*> mPrefetechedLayers;
+
+ // Stores the bounds of the main content.
+ Rect mContentOverdrawProtectionBounds;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 198906c..a47c9ec 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -38,9 +38,11 @@
DrawFrameTask::~DrawFrameTask() {
}
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+ RenderNode* targetNode) {
mRenderThread = thread;
mContext = context;
+ mTargetNode = targetNode;
}
void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -118,7 +120,7 @@
mContext->processLayerUpdate(mLayers[i].get());
}
mLayers.clear();
- mContext->prepareTree(info, mFrameInfo, mSyncQueued);
+ mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
// This is after the prepareTree so that any pending operations
// (RenderNode tree state, prefetched layers, etc...) will be flushed.
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index ebefcba..68ee897 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -57,7 +57,7 @@
DrawFrameTask();
virtual ~DrawFrameTask();
- void setContext(RenderThread* thread, CanvasContext* context);
+ void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
void pushLayerUpdate(DeferredLayerUpdater* layer);
void removeLayerUpdate(DeferredLayerUpdater* layer);
@@ -78,6 +78,7 @@
RenderThread* mRenderThread;
CanvasContext* mContext;
+ RenderNode* mTargetNode = nullptr;
/*********************************************
* Single frame data
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index d2ce49f..c9b9637 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -20,6 +20,7 @@
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
+#include "utils/StringUtils.h"
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -133,12 +134,9 @@
}
void EglManager::initExtensions() {
- std::string extensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
- auto has = [&](const char* ext) {
- return extensions.find(ext) != std::string::npos;
- };
- EglExtensions.bufferAge = has("EGL_EXT_buffer_age");
- EglExtensions.setDamage = has("EGL_KHR_partial_update");
+ StringCollection extensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+ EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age");
+ EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
}
bool EglManager::hasEglContext() {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b838811..f43a769 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -74,7 +74,7 @@
args->thread = &mRenderThread;
args->contextFactory = contextFactory;
mContext = (CanvasContext*) postAndWait(task);
- mDrawFrameTask.setContext(&mRenderThread, mContext);
+ mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
RenderProxy::~RenderProxy() {
@@ -91,7 +91,7 @@
SETUP_TASK(destroyContext);
args->context = mContext;
mContext = nullptr;
- mDrawFrameTask.setContext(nullptr, nullptr);
+ mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
postAndWait(task);
@@ -461,7 +461,8 @@
staticPostAndWait(task);
}
-CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) {
+CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map,
+ size_t size) {
CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size);
args->buffer->decStrong(nullptr);
return nullptr;
@@ -490,6 +491,61 @@
post(task);
}
+CREATE_BRIDGE3(addRenderNode, CanvasContext* context, RenderNode* node, bool placeFront) {
+ args->context->addRenderNode(args->node, args->placeFront);
+ return nullptr;
+}
+
+void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) {
+ SETUP_TASK(addRenderNode);
+ args->context = mContext;
+ args->node = node;
+ args->placeFront = placeFront;
+ post(task);
+}
+
+CREATE_BRIDGE2(removeRenderNode, CanvasContext* context, RenderNode* node) {
+ args->context->removeRenderNode(args->node);
+ return nullptr;
+}
+
+void RenderProxy::removeRenderNode(RenderNode* node) {
+ SETUP_TASK(removeRenderNode);
+ args->context = mContext;
+ args->node = node;
+ post(task);
+}
+
+CREATE_BRIDGE2(drawRenderNode, CanvasContext* context, RenderNode* node) {
+ args->context->prepareAndDraw(args->node);
+ return nullptr;
+}
+
+void RenderProxy::drawRenderNode(RenderNode* node) {
+ SETUP_TASK(drawRenderNode);
+ args->context = mContext;
+ args->node = node;
+ // Be pseudo-thread-safe and don't use any member variables
+ staticPostAndWait(task);
+}
+
+CREATE_BRIDGE5(setContentOverdrawProtectionBounds, CanvasContext* context, int left, int top,
+ int right, int bottom) {
+ args->context->setContentOverdrawProtectionBounds(args->left, args->top, args->right,
+ args->bottom);
+ return nullptr;
+}
+
+void RenderProxy::setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+ SETUP_TASK(setContentOverdrawProtectionBounds);
+ args->context = mContext;
+ args->left = left;
+ args->top = top;
+ args->right = right;
+ args->bottom = bottom;
+ staticPostAndWait(task);
+}
+
CREATE_BRIDGE1(serializeDisplayListTree, CanvasContext* context) {
args->context->serializeDisplayListTree();
return nullptr;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index e7356db..046f24a 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -106,6 +106,11 @@
ANDROID_API void serializeDisplayListTree();
+ ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
+ ANDROID_API void removeRenderNode(RenderNode* node);
+ ANDROID_API void drawRenderNode(RenderNode* node);
+ ANDROID_API void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom);
+
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/libs/hwui/unit_tests/StringUtilsTests.cpp b/libs/hwui/unit_tests/StringUtilsTests.cpp
new file mode 100644
index 0000000..5174ae9
--- /dev/null
+++ b/libs/hwui/unit_tests/StringUtilsTests.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "utils/StringUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+TEST(StringUtils, simpleBuildSet) {
+ StringCollection collection("a b c");
+
+ EXPECT_TRUE(collection.has("a"));
+ EXPECT_TRUE(collection.has("b"));
+ EXPECT_TRUE(collection.has("c"));
+ EXPECT_FALSE(collection.has("d"));
+}
+
+TEST(StringUtils, advancedBuildSet) {
+ StringCollection collection("GL_ext1 GL_ext2 GL_ext3");
+
+ EXPECT_TRUE(collection.has("GL_ext1"));
+ EXPECT_FALSE(collection.has("GL_ext")); // string present, but not in list
+}
+
+};
+};
diff --git a/libs/hwui/utils/StringUtils.cpp b/libs/hwui/utils/StringUtils.cpp
new file mode 100644
index 0000000..a1df0e7
--- /dev/null
+++ b/libs/hwui/utils/StringUtils.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "StringUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+StringCollection::StringCollection(const char* spacedList) {
+ const char* current = spacedList;
+ const char* head = current;
+ do {
+ head = strchr(current, ' ');
+ std::string s(current, head ? head - current : strlen(current));
+ if (s.length()) {
+ mSet.insert(s);
+ }
+ current = head + 1;
+ } while (head);
+}
+
+bool StringCollection::has(const char* s) {
+ return mSet.find(std::string(s)) != mSet.end();
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h
new file mode 100644
index 0000000..ef2a6d5
--- /dev/null
+++ b/libs/hwui/utils/StringUtils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef STRING_UTILS_H
+#define STRING_UTILS_H
+
+#include <string>
+#include <unordered_set>
+
+namespace android {
+namespace uirenderer {
+
+class StringCollection {
+public:
+ StringCollection(const char* spacedList);
+ bool has(const char* string);
+private:
+ std::unordered_set<std::string> mSet;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* GLUTILS_H */
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index e72bdb4..266b0d9 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -169,6 +169,13 @@
/**
* Registers a callback to receive notifications when MIDI devices are added and removed.
*
+ * The {@link DeviceCallback#onDeviceStatusChanged} method will be called immediately
+ * for any devices that have open ports. This allows applications to know which input
+ * ports are already in use and, therefore, unavailable.
+ *
+ * Applications should call {@link #getDevices} before registering the callback
+ * to get a list of devices already added.
+ *
* @param callback a {@link DeviceCallback} for MIDI device notifications
* @param handler The {@link android.os.Handler Handler} that will be used for delivering the
* device notifications. If handler is null, then the thread used for the
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index 3cd157e..95cb520 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -18,6 +18,8 @@
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
/**
@@ -47,7 +49,7 @@
/**
* Opens the MTP device. Once the device is open it takes ownership of the
- * {@link android.hardware.usb.UsbDeviceConnection}.
+ * {@link android.hardware.usb.UsbDeviceConnection}.
* The connection will be closed when you call {@link #close()}
* The connection will also be closed if this method fails.
*
@@ -278,6 +280,38 @@
return native_send_object_info(info);
}
+ /**
+ * Reads an event from the device. It blocks the current thread until it gets an event.
+ * It throws OperationCanceledException if it is cancelled by signal.
+ *
+ * @param signal signal for cancellation
+ * @return obtained event
+ */
+ public MtpEvent readEvent(CancellationSignal signal) {
+ final int handle = native_submit_event_request();
+
+ if (handle < 0) {
+ throw new IllegalStateException("Other thread is reading an event.");
+ }
+
+ if (signal != null) {
+ signal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
+ @Override
+ public void onCancel() {
+ native_discard_event_request(handle);
+ }
+ });
+ }
+
+ try {
+ return native_reap_event_request(handle);
+ } finally {
+ if (signal != null) {
+ signal.setOnCancelListener(null);
+ }
+ }
+ }
+
// used by the JNI code
private long mNativeContext;
@@ -297,4 +331,7 @@
private native boolean native_import_file(int objectHandle, int fd);
private native boolean native_send_object(int objectHandle, int size, int fd);
private native MtpObjectInfo native_send_object_info(MtpObjectInfo info);
+ private native int native_submit_event_request();
+ private native MtpEvent native_reap_event_request(int handle);
+ private native void native_discard_event_request(int handle);
}
diff --git a/media/java/android/mtp/MtpEvent.java b/media/java/android/mtp/MtpEvent.java
new file mode 100644
index 0000000..0fa7d93
--- /dev/null
+++ b/media/java/android/mtp/MtpEvent.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mtp;
+
+/**
+ * This class encapsulates information about a MTP event.
+ */
+public class MtpEvent {
+ private int mEventCode;
+
+ /**
+ * Returns event code of MTP event.
+ *
+ * @return event code
+ */
+ public int getEventCode() { return mEventCode; }
+}
diff --git a/media/jni/android_media_AmrInputStream.cpp b/media/jni/android_media_AmrInputStream.cpp
index afb5d5c..b56a364 100644
--- a/media/jni/android_media_AmrInputStream.cpp
+++ b/media/jni/android_media_AmrInputStream.cpp
@@ -119,7 +119,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"GsmAmrEncoderNew", "()J", (void*)android_media_AmrInputStream_GsmAmrEncoderNew},
{"GsmAmrEncoderInitialize", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderInitialize},
{"GsmAmrEncoderEncode", "(J[BI[BI)I", (void*)android_media_AmrInputStream_GsmAmrEncoderEncode},
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 0034b07..3ffdb17 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -1253,7 +1253,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gImageReaderMethods[] = {
+static const JNINativeMethod gImageReaderMethods[] = {
{"nativeClassInit", "()V", (void*)ImageReader_classInit },
{"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init },
{"nativeClose", "()V", (void*)ImageReader_close },
@@ -1263,7 +1263,7 @@
{"nativeDetachImage", "(Landroid/media/Image;)I", (void*)ImageReader_detachImage },
};
-static JNINativeMethod gImageMethods[] = {
+static const JNINativeMethod gImageMethods[] = {
{"nativeImageGetBuffer", "(II)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer },
{"nativeCreatePlane", "(II)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
(void*)Image_createSurfacePlane },
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 93a4426..e8f680f 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1798,7 +1798,7 @@
android_media_MediaCodec_release(env, thiz);
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "native_release", "()V", (void *)android_media_MediaCodec_release },
{ "native_reset", "()V", (void *)android_media_MediaCodec_reset },
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index 82dd48d..de9bf1f 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -286,7 +286,7 @@
static void android_media_MediaCodecList_native_init(JNIEnv* /* env */) {
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "native_getCodecCount", "()I", (void *)android_media_MediaCodecList_getCodecCount },
{ "getCodecName", "(I)Ljava/lang/String;",
(void *)android_media_MediaCodecList_getCodecName },
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index d7968d2..e414f48 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -316,7 +316,7 @@
}
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "release", "()V", (void *)android_media_MediaCrypto_release },
{ "native_init", "()V", (void *)android_media_MediaCrypto_native_init },
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 9ec0312..275de1ad 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1455,7 +1455,7 @@
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "release", "()V", (void *)android_media_MediaDrm_release },
{ "native_init", "()V", (void *)android_media_MediaDrm_native_init },
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 4e9b726..96c12dd 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -769,7 +769,7 @@
android_media_MediaExtractor_release(env, thiz);
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "release", "()V", (void *)android_media_MediaExtractor_release },
{ "getTrackCount", "()I", (void *)android_media_MediaExtractor_getTrackCount },
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index 393003d..fa0b43f 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -154,7 +154,7 @@
return n;
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "native_getIMemory", "()Landroid/os/IBinder;",
(void *)android_media_MediaHTTPConnection_native_getIMemory },
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 59fb6d6..f4e940d 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -467,7 +467,7 @@
}
// JNI mapping between Java methods and native methods
-static JNINativeMethod nativeMethods[] = {
+static const JNINativeMethod nativeMethods[] = {
{
"_setDataSource",
"(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
index ecb2ac8..216624e 100644
--- a/media/jni/android_media_MediaMuxer.cpp
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -219,7 +219,7 @@
}
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nativeAddTrack", "(J[Ljava/lang/String;[Ljava/lang/Object;)I",
(void *)android_media_MediaMuxer_addTrack },
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index d8041f4..be36729 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1033,7 +1033,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{
"nativeSetDataSource",
"(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index ca9db91..5800043 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -301,7 +301,7 @@
}
return static_cast<jint>(levels[index]);
}
-static JNINativeMethod gMethodsForEncoderCapabilitiesClass[] = {
+static const JNINativeMethod gMethodsForEncoderCapabilitiesClass[] = {
{"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
{"native_get_num_file_formats", "()I", (void *)android_media_MediaProfiles_native_get_num_file_formats},
{"native_get_file_format", "(I)I", (void *)android_media_MediaProfiles_native_get_file_format},
@@ -315,7 +315,7 @@
(void *)android_media_MediaProfiles_native_get_audio_encoder_cap},
};
-static JNINativeMethod gMethodsForCamcorderProfileClass[] = {
+static const JNINativeMethod gMethodsForCamcorderProfileClass[] = {
{"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
{"native_get_camcorder_profile", "(II)Landroid/media/CamcorderProfile;",
(void *)android_media_MediaProfiles_native_get_camcorder_profile},
@@ -323,7 +323,7 @@
(void *)android_media_MediaProfiles_native_has_camcorder_profile},
};
-static JNINativeMethod gMethodsForDecoderCapabilitiesClass[] = {
+static const JNINativeMethod gMethodsForDecoderCapabilitiesClass[] = {
{"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
{"native_get_num_video_decoders", "()I", (void *)android_media_MediaProfiles_native_get_num_video_decoders},
{"native_get_num_audio_decoders", "()I", (void *)android_media_MediaProfiles_native_get_num_audio_decoders},
@@ -331,7 +331,7 @@
{"native_get_audio_decoder_type", "(I)I", (void *)android_media_MediaProfiles_native_get_audio_decoder_type},
};
-static JNINativeMethod gMethodsForCameraProfileClass[] = {
+static const JNINativeMethod gMethodsForCameraProfileClass[] = {
{"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
{"native_get_num_image_encoding_quality_levels",
"(I)I", (void *)android_media_MediaProfiles_native_get_num_image_encoding_quality_levels},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index f60af63..e05b348 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -510,7 +510,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"setCamera", "(Landroid/hardware/Camera;)V", (void *)android_media_MediaRecorder_setCamera},
{"setVideoSource", "(I)V", (void *)android_media_MediaRecorder_setVideoSource},
{"setAudioSource", "(I)V", (void *)android_media_MediaRecorder_setAudioSource},
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index 1a9384e..0f3c61f 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -412,7 +412,7 @@
setNativeScanner_l(env, thiz, 0);
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{
"processDirectory",
"(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
diff --git a/media/jni/android_media_ResampleInputStream.cpp b/media/jni/android_media_ResampleInputStream.cpp
index 1549a30..d06baa5 100644
--- a/media/jni/android_media_ResampleInputStream.cpp
+++ b/media/jni/android_media_ResampleInputStream.cpp
@@ -107,7 +107,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"fir21", "([BI[BII)V", (void*)android_media_ResampleInputStream_fir21},
};
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 713f28c..ec2f98a 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -1173,12 +1173,12 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMtpDatabaseMethods[] = {
+static const JNINativeMethod gMtpDatabaseMethods[] = {
{"native_setup", "()V", (void *)android_mtp_MtpDatabase_setup},
{"native_finalize", "()V", (void *)android_mtp_MtpDatabase_finalize},
};
-static JNINativeMethod gMtpPropertyGroupMethods[] = {
+static const JNINativeMethod gMtpPropertyGroupMethods[] = {
{"format_date_time", "(J)Ljava/lang/String;",
(void *)android_mtp_MtpPropertyGroup_format_date_time},
};
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 2a46ee7..3f4d183 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -46,10 +46,14 @@
jclass clazz_deviceInfo;
jclass clazz_storageInfo;
jclass clazz_objectInfo;
+jclass clazz_event;
+jclass clazz_io_exception;
+jclass clazz_operation_canceled_exception;
jmethodID constructor_deviceInfo;
jmethodID constructor_storageInfo;
jmethodID constructor_objectInfo;
+jmethodID constructor_event;
// MtpDeviceInfo fields
static jfieldID field_deviceInfo_manufacturer;
@@ -86,6 +90,9 @@
static jfieldID field_objectInfo_dateModified;
static jfieldID field_objectInfo_keywords;
+// MtpEvent fields
+static jfieldID field_event_eventCode;
+
MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice)
{
return (MtpDevice*)env->GetLongField(javaDevice, field_context);
@@ -488,9 +495,45 @@
return result;
}
+static jint android_mtp_MtpDevice_submit_event_request(JNIEnv *env, jobject thiz)
+{
+ MtpDevice* const device = get_device_from_object(env, thiz);
+ if (!device) {
+ env->ThrowNew(clazz_io_exception, "");
+ return -1;
+ }
+ return device->submitEventRequest();
+}
+
+static jobject android_mtp_MtpDevice_reap_event_request(JNIEnv *env, jobject thiz, jint seq)
+{
+ MtpDevice* const device = get_device_from_object(env, thiz);
+ if (!device) {
+ env->ThrowNew(clazz_io_exception, "");
+ return NULL;
+ }
+ const int eventCode = device->reapEventRequest(seq);
+ if (eventCode <= 0) {
+ env->ThrowNew(clazz_operation_canceled_exception, "");
+ return NULL;
+ }
+ jobject result = env->NewObject(clazz_event, constructor_event);
+ env->SetIntField(result, field_event_eventCode, eventCode);
+ return result;
+}
+
+static void android_mtp_MtpDevice_discard_event_request(JNIEnv *env, jobject thiz, jint seq)
+{
+ MtpDevice* const device = get_device_from_object(env, thiz);
+ if (!device) {
+ return;
+ }
+ device->discardEventRequest(seq);
+}
+
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"native_open", "(Ljava/lang/String;I)Z",
(void *)android_mtp_MtpDevice_open},
{"native_close", "()V", (void *)android_mtp_MtpDevice_close},
@@ -513,7 +556,11 @@
{"native_import_file", "(II)Z",(void *)android_mtp_MtpDevice_import_file_to_fd},
{"native_send_object", "(III)Z",(void *)android_mtp_MtpDevice_send_object},
{"native_send_object_info", "(Landroid/mtp/MtpObjectInfo;)Landroid/mtp/MtpObjectInfo;",
- (void *)android_mtp_MtpDevice_send_object_info}
+ (void *)android_mtp_MtpDevice_send_object_info},
+ {"native_submit_event_request", "()I", (void *)android_mtp_MtpDevice_submit_event_request},
+ {"native_reap_event_request", "(I)Landroid/mtp/MtpEvent;",
+ (void *)android_mtp_MtpDevice_reap_event_request},
+ {"native_discard_event_request", "(I)V", (void *)android_mtp_MtpDevice_discard_event_request},
};
int register_android_mtp_MtpDevice(JNIEnv *env)
@@ -703,6 +750,23 @@
}
clazz_objectInfo = (jclass)env->NewGlobalRef(clazz);
+ clazz = env->FindClass("android/mtp/MtpEvent");
+ if (clazz == NULL) {
+ ALOGE("Can't find android/mtp/MtpEvent");
+ return -1;
+ }
+ constructor_event = env->GetMethodID(clazz, "<init>", "()V");
+ if (constructor_event == NULL) {
+ ALOGE("Can't find android/mtp/MtpEvent constructor");
+ return -1;
+ }
+ field_event_eventCode = env->GetFieldID(clazz, "mEventCode", "I");
+ if (field_event_eventCode == NULL) {
+ ALOGE("Can't find MtpObjectInfo.mEventCode");
+ return -1;
+ }
+ clazz_event = (jclass)env->NewGlobalRef(clazz);
+
clazz = env->FindClass("android/mtp/MtpDevice");
if (clazz == NULL) {
ALOGE("Can't find android/mtp/MtpDevice");
@@ -713,6 +777,18 @@
ALOGE("Can't find MtpDevice.mNativeContext");
return -1;
}
+ clazz = env->FindClass("java/io/IOException");
+ if (clazz == NULL) {
+ ALOGE("Can't find java.io.IOException");
+ return -1;
+ }
+ clazz_io_exception = (jclass)env->NewGlobalRef(clazz);
+ clazz = env->FindClass("android/os/OperationCanceledException");
+ if (clazz == NULL) {
+ ALOGE("Can't find android.os.OperationCanceledException");
+ return -1;
+ }
+ clazz_operation_canceled_exception = (jclass)env->NewGlobalRef(clazz);
return AndroidRuntime::registerNativeMethods(env,
"android/mtp/MtpDevice", gMethods, NELEM(gMethods));
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index 2ce2a90..d13187c 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -179,7 +179,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"native_setup", "(Landroid/mtp/MtpDatabase;Z)V",
(void *)android_mtp_MtpServer_setup},
{"native_run", "()V", (void *)android_mtp_MtpServer_run},
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index aba4bbe..fa69135 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -879,7 +879,7 @@
// ----------------------------------------------------------------------------
// Dalvik VM type signatures
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_AudioEffect_native_init},
{"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;Ljava/lang/String;)I",
(void *)android_media_AudioEffect_native_setup},
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 0557019..3d3adba 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -677,7 +677,7 @@
// ----------------------------------------------------------------------------
// Dalvik VM type signatures
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_visualizer_native_init},
{"native_setup", "(Ljava/lang/Object;I[ILjava/lang/String;)I",
(void *)android_media_visualizer_native_setup},
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index a705bcc..090be88 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -589,6 +589,9 @@
ALOGV("format changed to: %s", AMediaFormat_toString(format));
} else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
ALOGV("no output buffer right now");
+ } else if (status <= AMEDIA_ERROR_BASE) {
+ ALOGE("decode error: %d", status);
+ break;
} else {
ALOGV("unexpected info code: %d", status);
}
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index e6d59e4..444705c 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -147,14 +147,22 @@
// switch to receiving notifications after initial characteristic read
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
+ // Use writeType that requests acknowledgement.
+ // This improves compatibility with various BLE-MIDI devices.
+ int originalWriteType = characteristic.getWriteType();
+ characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
+
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
CLIENT_CHARACTERISTIC_CONFIG);
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
- mBluetoothGatt.writeDescriptor(descriptor);
+ boolean result = mBluetoothGatt.writeDescriptor(descriptor);
+ Log.d(TAG, "writeDescriptor returned " + result);
} else {
Log.e(TAG, "No CLIENT_CHARACTERISTIC_CONFIG for device " + mBluetoothDevice);
}
+
+ characteristic.setWriteType(originalWriteType);
}
@Override
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
index 1b684bb..175f730 100644
--- a/native/graphics/jni/Android.mk
+++ b/native/graphics/jni/Android.mk
@@ -31,7 +31,7 @@
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-# TODO: This is to work around b/19059885. Remove after root cause is fixed
+# TODO: This is to work around b/24465209. Remove after root cause is fixed
LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
include $(BUILD_SHARED_LIBRARY)
diff --git a/packages/BackupRestoreConfirmation/res/values-nl/strings.xml b/packages/BackupRestoreConfirmation/res/values-nl/strings.xml
index f483b14..81f2712 100644
--- a/packages/BackupRestoreConfirmation/res/values-nl/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-nl/strings.xml
@@ -18,18 +18,18 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"Volledige back-up"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Volledig herstel"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"Er is een volledige back-up van alle gegevens naar een verbonden desktopcomputer aangevraagd. Wilt u dit toestaan?\n\nAls u de back-up zelf niet heeft aangevraagd, moet u niet toestaan dat de bewerking wordt uitgevoerd."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"Er is een volledige back-up van alle gegevens naar een verbonden desktopcomputer aangevraagd. Wil je dit toestaan?\n\nAls je de back-up niet zelf hebt aangevraagd, moet je niet toestaan dat de bewerking wordt uitgevoerd."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Back-up maken van mijn gegevens"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Geen back-up maken"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"Er is volledig herstel van alle gegevens van een verbonden desktopcomputer aangevraagd. Wilt u dit toestaan?\n\nAls u het herstel zelf niet heeft aangevraagd, moet u niet toestaan dat de bewerking wordt uitgevoerd. Bij herstel worden alle gegevens op het apparaat vervangen."</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"Er is volledig herstel van alle gegevens van een verbonden desktopcomputer aangevraagd. Wil je dit toestaan?\n\nAls je het herstel niet zelf hebt aangevraagd, moet je niet toestaan dat de bewerking wordt uitgevoerd. Bij herstel worden alle gegevens op het apparaat vervangen."</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Mijn gegevens herstellen"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"Niet herstellen"</string>
- <string name="current_password_text" msgid="8268189555578298067">"Geef hieronder uw huidige back-upwachtwoord op:"</string>
- <string name="device_encryption_restore_text" msgid="1570864916855208992">"Geef hieronder uw wachtwoord voor apparaatcodering op."</string>
- <string name="device_encryption_backup_text" msgid="5866590762672844664">"Geef hieronder uw wachtwoord voor apparaatversleuteling op. Dit wordt ook gebruikt om het back-uparchief te versleutelen."</string>
- <string name="backup_enc_password_text" msgid="4981585714795233099">"Geef een wachtwoord op dat u wilt gebruiken voor het coderen van de gegevens van de volledige back-up. Als u dit leeg laat, wordt uw huidige back-upwachtwoord gebruikt:"</string>
+ <string name="current_password_text" msgid="8268189555578298067">"Geef hieronder je huidige back-upwachtwoord op:"</string>
+ <string name="device_encryption_restore_text" msgid="1570864916855208992">"Geef hieronder je wachtwoord voor apparaatcodering op."</string>
+ <string name="device_encryption_backup_text" msgid="5866590762672844664">"Geef hieronder je wachtwoord voor apparaatversleuteling op. Dit wordt ook gebruikt om het back-uparchief te versleutelen."</string>
+ <string name="backup_enc_password_text" msgid="4981585714795233099">"Geef een wachtwoord op dat u wilt gebruiken voor het coderen van de gegevens van de volledige back-up. Als u dit leeg laat, wordt je huidige back-upwachtwoord gebruikt:"</string>
<string name="backup_enc_password_optional" msgid="1350137345907579306">"Als u de gegevens van de volledige back-up wilt versleutelen, geeft u daarvoor hieronder een wachtwoord op:"</string>
- <string name="backup_enc_password_required" msgid="7889652203371654149">"Aangezien uw apparaat is gecodeerd, moet u uw back-up coderen. Geef hieronder een wachtwoord op:"</string>
+ <string name="backup_enc_password_required" msgid="7889652203371654149">"Aangezien je apparaat is gecodeerd, moet u je back-up coderen. Geef hieronder een wachtwoord op:"</string>
<string name="restore_enc_password_text" msgid="6140898525580710823">"Als deze herstelgegevens zijn gecodeerd, geeft u hieronder het wachtwoord op:"</string>
<string name="toast_backup_started" msgid="550354281452756121">"Back-up starten..."</string>
<string name="toast_backup_ended" msgid="3818080769548726424">"Back-up voltooid"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
index 969ec599..6a7dbbd 100644
--- a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"Copiere de rezervă completă"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Restabilire completă"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unei copii de rezervă complete a tuturor datelor pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu aţi solicitat dvs. copierea de rezervă, nu permiteți ca operaţiunea să continue."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unei copii de rezervă complete a tuturor datelor pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu aţi solicitat dvs. copierea de rezervă, nu permiteți ca operațiunea să continue."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Creaţi copii de rezervă pentru datele dvs."</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Nu creaţi copii de rezervă"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu dvs. aţi solicitat această restabilire, nu permiteți continuarea operaţiunii. Acest proces va înlocui toate datele existente în prezent pe dispozitiv!"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu dvs. aţi solicitat această restabilire, nu permiteți continuarea operațiunii. Acest proces va înlocui toate datele existente în prezent pe dispozitiv!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Restabiliţi datele dvs."</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"Nu restabiliţi"</string>
<string name="current_password_text" msgid="8268189555578298067">"Introduceţi mai jos parola actuală pentru copia de rezervă:"</string>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 0fe5509..abb464e 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -76,8 +76,9 @@
String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
if (server == null) server = DEFAULT_SERVER;
mCm = ConnectivityManager.from(this);
+ String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
try {
- mURL = new URL("http", server, "/generate_204");
+ mURL = url != null ? new URL(url) : new URL("http", server, "/generate_204");
} catch (MalformedURLException e) {
// System misconfigured, bail out in a way that at least provides network access.
Log.e(TAG, "Invalid captive portal URL, server=" + server);
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index d578769..295cb80 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -3,6 +3,7 @@
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.REMOVE_TASKS" />
+ <uses-permission android:name="android.permission.REORDER_TASKS" />
<application
android:name=".DocumentsApplication"
@@ -35,15 +36,28 @@
<action android:name="android.intent.action.OPEN_DOCUMENT_TREE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
+ </activity>
+
+ <activity
+ android:name=".ManageRootActivity"
+ android:theme="@style/DocumentsNonDialogTheme"
+ android:icon="@drawable/ic_doc_text">
<intent-filter>
<action android:name="android.provider.action.MANAGE_ROOT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.document/root" />
</intent-filter>
+ </activity>
+
+ <activity
+ android:name=".LauncherActivity"
+ android:theme="@android:style/Theme.NoDisplay"
+ android:icon="@drawable/ic_files_app"
+ android:label="@string/files_label"
+ android:enabled="@bool/productivity_device">
<intent-filter>
- <action android:name="android.provider.action.BROWSE_DOCUMENT_ROOT" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.document/root" />
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@@ -52,10 +66,14 @@
android:theme="@style/FilesTheme"
android:icon="@drawable/ic_files_app"
android:label="@string/files_label"
- android:enabled="@bool/productivity_device">
+ android:documentLaunchMode="intoExisting">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.provider.action.BROWSE_DOCUMENT_ROOT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.document/root" />
</intent-filter>
</activity>
diff --git a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
index 6959c65..6dcbb38 100644
--- a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
+++ b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
@@ -17,7 +17,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_activated="true"
- android:color="?android:attr/colorControlHighlight" />
+ android:color="?android:attr/colorAccent"
+ android:alpha="0.1" />
<item
android:state_enabled="false"
android:color="?android:attr/colorBackground"
diff --git a/packages/DocumentsUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml b/packages/DocumentsUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
new file mode 100644
index 0000000..070b9a1
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Variant of progress_indeterminate_horizontal_material in frameworks/base/core/res, which
+ draws the whole height of the progress bar instead having blank space above and below the
+ bar. -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal_trimmed" >
+ <target
+ android:name="rect2_grp"
+ android:animation="@*android:anim/progress_indeterminate_horizontal_rect2" />
+ <target
+ android:name="rect1_grp"
+ android:animation="@*android:anim/progress_indeterminate_horizontal_rect1" />
+</animated-vector>
diff --git a/packages/DocumentsUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml b/packages/DocumentsUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
new file mode 100644
index 0000000..39e3a37
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Variant of vector_drawable_progress_indeterminate_horizontal in frameworks/base/core/res, which
+ draws the whole height of the progress bar instead having blank space above and below the
+ bar. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="10dp"
+ android:width="360dp"
+ android:viewportHeight="10"
+ android:viewportWidth="360" >
+ <group
+ android:name="progress_group"
+ android:translateX="180"
+ android:translateY="5" >
+ <path
+ android:name="background_track"
+ android:pathData="M -180.0,-5.0 l 360.0,0 l 0,10.0 l -360.0,0 Z"
+ android:fillColor="?android:attr/colorControlActivated"
+ android:fillAlpha="?android:attr/disabledAlpha"/>
+ <group
+ android:name="rect2_grp"
+ android:translateX="-197.60001"
+ android:scaleX="0.1" >
+ <path
+ android:name="rect2"
+ android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+ android:fillColor="?android:attr/colorControlActivated" />
+ </group>
+ <group
+ android:name="rect1_grp"
+ android:translateX="-522.59998"
+ android:scaleX="0.1" >
+ <path
+ android:name="rect1"
+ android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+ android:fillColor="?android:attr/colorControlActivated" />
+ </group>
+ </group>
+</vector>
diff --git a/packages/DocumentsUI/res/layout/directory_cluster.xml b/packages/DocumentsUI/res/layout/directory_cluster.xml
new file mode 100644
index 0000000..8245e53
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/directory_cluster.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/container_message_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:elevation="8dp"
+ android:background="@color/material_grey_50"/>
+
+ <com.android.documentsui.DirectoryContainerView
+ android:id="@+id/container_directory"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <FrameLayout
+ android:id="@+id/container_save"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/material_grey_50"
+ android:elevation="8dp" />
+
+</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml
index 32431e3..dec4e92 100644
--- a/packages/DocumentsUI/res/layout/drawer_layout.xml
+++ b/packages/DocumentsUI/res/layout/drawer_layout.xml
@@ -30,7 +30,8 @@
android:layout_height="?android:attr/actionBarSize"
android:background="?android:attr/colorPrimary"
android:elevation="8dp"
- android:theme="?android:attr/actionBarTheme">
+ android:theme="?actionBarTheme"
+ android:popupTheme="?actionBarPopupTheme">
<Spinner
android:id="@+id/stack"
@@ -41,18 +42,7 @@
</com.android.documentsui.DocumentsToolBar>
- <com.android.documentsui.DirectoryContainerView
- android:id="@+id/container_directory"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
- <FrameLayout
- android:id="@+id/container_save"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/material_grey_50"
- android:elevation="8dp" />
+ <include layout="@layout/directory_cluster"/>
</LinearLayout>
@@ -71,7 +61,8 @@
android:layout_height="?android:attr/actionBarSize"
android:background="?android:attr/colorPrimary"
android:elevation="8dp"
- android:theme="?android:attr/actionBarTheme" />
+ android:theme="?actionBarTheme"
+ android:popupTheme="?actionBarPopupTheme" />
<FrameLayout
android:id="@+id/container_roots"
diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml
index 9769f26..eba9af4 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -51,28 +51,11 @@
android:layout_width="256dp"
android:layout_height="match_parent" />
- <LinearLayout
+ <include layout="@layout/directory_cluster"
android:layout_width="0dp"
- android:layout_height="match_parent"
android:layout_weight="1"
- android:orientation="vertical"
- android:background="@color/material_grey_50"
- android:elevation="8dp">
-
- <com.android.documentsui.DirectoryContainerView
- android:id="@+id/container_directory"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
- <FrameLayout
- android:id="@+id/container_save"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/material_grey_50"
- android:elevation="8dp" />
-
- </LinearLayout>
+ android:elevation="8dp"
+ android:background="@color/material_grey_50" />
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index 7aff497..45cb34d 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -17,35 +17,70 @@
<com.android.documentsui.DirectoryView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/material_grey_50">
+ android:background="@color/material_grey_50"
+ android:orientation="vertical"
+ android:animateLayoutChanges="true">
- <TextView
+ <ProgressBar
+ android:id="@+id/progressbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ style="@style/TrimmedHorizontalProgressBar"
+ android:visibility="gone"/>
+
+ <FrameLayout
+ android:id="@+id/container_message_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:elevation="8dp"
+ android:background="@color/material_grey_50"
+ android:visibility="gone"/>
+
+ <!-- The empty directory view -->
+ <LinearLayout
android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
android:gravity="center"
- android:text="@string/empty"
- android:visibility="gone"
- style="@android:style/TextAppearance.Material.Subhead" />
-
- <!-- The 'list' view is still used for RecentsCreateFragment -->
- <ListView
- android:id="@+id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <android.support.v7.widget.RecyclerView
- android:id="@+id/recyclerView"
- android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingStart="@dimen/grid_padding_horiz"
- android:paddingEnd="@dimen/grid_padding_horiz"
- android:paddingTop="@dimen/grid_padding_vert"
- android:paddingBottom="@dimen/grid_padding_vert"
- android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
- android:drawSelectorOnTop="true"
- android:background="@color/directory_background" />
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/empty"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ <Button
+ android:id="@+id/button_retry"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button_retry"
+ style="?android:attr/buttonBarPositiveButtonStyle" />
+
+ </LinearLayout>
+
+ <!-- This FrameLayout works around b/24189541 -->
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/recyclerView"
+ android:scrollbars="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/grid_padding_horiz"
+ android:paddingEnd="@dimen/grid_padding_horiz"
+ android:paddingTop="@dimen/grid_padding_vert"
+ android:paddingBottom="@dimen/grid_padding_vert"
+ android:clipToPadding="false"
+ android:scrollbarStyle="outsideOverlay"
+ android:drawSelectorOnTop="true"
+ android:background="@color/directory_background" />
+
+ </FrameLayout>
</com.android.documentsui.DirectoryView>
diff --git a/packages/DocumentsUI/res/layout/fragment_message_bar.xml b/packages/DocumentsUI/res/layout/fragment_message_bar.xml
new file mode 100644
index 0000000..47e4e77
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/fragment_message_bar.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:animateLayoutChanges="true"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:orientation="vertical"
+ android:paddingLeft="@dimen/list_item_padding"
+ android:paddingRight="@dimen/list_item_padding"
+ android:paddingTop="@dimen/list_item_padding">
+
+ <LinearLayout
+ android:id="@+id/container_info"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone">
+
+ <FrameLayout
+ android:layout_height="@dimen/icon_size"
+ android:layout_width="@dimen/icon_size">
+
+ <ImageView
+ android:contentDescription="@null"
+ android:id="@+id/icon_info"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:scaleType="centerInside"/>
+
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/textview_info"
+ android:layout_gravity="center_vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:selectAllOnFocus="true"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/container_error"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone">
+
+ <FrameLayout
+ android:layout_height="@dimen/icon_size"
+ android:layout_width="@dimen/icon_size">
+
+ <ImageView
+ android:contentDescription="@null"
+ android:id="@+id/icon_error"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:scaleType="centerInside"/>
+
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/textview_error"
+ android:layout_gravity="center_vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:selectAllOnFocus="true"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_gravity="right"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/button_dismiss"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button_dismiss"
+ style="?android:attr/buttonBarPositiveButtonStyle"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/item_loading_grid.xml b/packages/DocumentsUI/res/layout/item_loading_grid.xml
deleted file mode 100644
index 147dfd4..0000000
--- a/packages/DocumentsUI/res/layout/item_loading_grid.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/grid_height"
- android:orientation="horizontal">
-
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:indeterminate="true"
- style="?android:attr/progressBarStyle" />
-
-</com.android.documentsui.GridItem>
diff --git a/packages/DocumentsUI/res/layout/item_message_grid.xml b/packages/DocumentsUI/res/layout/item_message_grid.xml
deleted file mode 100644
index 45d61a5..0000000
--- a/packages/DocumentsUI/res/layout/item_message_grid.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/grid_height"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="8dp"
- android:paddingBottom="8dp">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@null" />
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:maxLines="4"
- android:ellipsize="end"
- android:paddingTop="8dp"
- android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Body1"
- android:textColor="?android:attr/textColorPrimary" />
-
- </LinearLayout>
-
-</com.android.documentsui.GridItem>
diff --git a/packages/DocumentsUI/res/layout/item_message_list.xml b/packages/DocumentsUI/res/layout/item_message_list.xml
deleted file mode 100644
index 44c8baf..0000000
--- a/packages/DocumentsUI/res/layout/item_message_list.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/list_item_height"
- android:paddingStart="@dimen/list_item_padding"
- android:paddingEnd="@dimen/list_item_padding"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:baselineAligned="false">
-
- <FrameLayout
- android:layout_width="@dimen/icon_size"
- android:layout_height="@dimen/icon_size"
- android:layout_marginEnd="16dp">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:contentDescription="@null" />
-
- </FrameLayout>
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:maxLines="2"
- android:ellipsize="end"
- android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Body1"
- android:textColor="?android:attr/textColorPrimary" />
-
-</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/single_pane_layout.xml b/packages/DocumentsUI/res/layout/single_pane_layout.xml
new file mode 100644
index 0000000..20c3232
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/single_pane_layout.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.android.documentsui.DocumentsToolBar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:background="?android:attr/colorPrimary"
+ android:elevation="8dp"
+ android:theme="?actionBarTheme"
+ android:popupTheme="?actionBarPopupTheme">
+
+ <Spinner
+ android:id="@+id/stack"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:overlapAnchor="true" />
+
+ </com.android.documentsui.DocumentsToolBar>
+
+ <include layout="@layout/directory_cluster"/>
+
+</LinearLayout>
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index e1f6562..673a254 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -26,7 +26,9 @@
android:id="@+id/menu_create_dir"
android:title="@string/menu_create_dir"
android:icon="@drawable/ic_menu_new_folder"
- android:showAsAction="always" />
+ android:alphabeticShortcut="e"
+ android:showAsAction="always"
+ android:visible="false" />
<item
android:id="@+id/menu_sort"
android:title="@string/menu_sort"
@@ -55,17 +57,26 @@
android:icon="@drawable/ic_menu_view_list"
android:showAsAction="never" />
<item
+ android:id="@+id/menu_new_window"
+ android:title="@string/menu_new_window"
+ android:alphabeticShortcut="n"
+ android:showAsAction="never"
+ android:visible="false" />
+ <item
android:id="@+id/menu_paste_from_clipboard"
android:title="@string/menu_paste_from_clipboard"
android:alphabeticShortcut="v"
android:showAsAction="never"
android:visible="false" />
+ <!-- Copy action is defined in mode_directory.xml -->
<item
android:id="@+id/menu_advanced"
- android:showAsAction="never" />
+ android:showAsAction="never"
+ android:visible="false" />
<item
android:id="@+id/menu_file_size"
- android:showAsAction="never" />
+ android:showAsAction="never"
+ android:visible="false" />
<item
android:id="@+id/menu_settings"
android:title="@string/menu_settings"
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index 0053f22..c2b6dbc 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Kies"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopieer"</string>
<string name="button_move" msgid="2202666023104202232">"Skuif"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Maak toe"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Probeer weer"</string>
<string name="sort_name" msgid="9183560467917256779">"Volgens naam"</string>
<string name="sort_date" msgid="586080032956151448">"Volgens datum gewysig"</string>
<string name="sort_size" msgid="3350681319735474741">"Volgens grootte"</string>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index eb88830..b82fa93 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"ምረጥ"</string>
<string name="button_copy" msgid="8706475544635021302">"ቅዳ"</string>
<string name="button_move" msgid="2202666023104202232">"አንቀሳቀስ"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"አሰናብት"</string>
+ <string name="button_retry" msgid="4392027584153752797">"እንደገና ይሞክሩ"</string>
<string name="sort_name" msgid="9183560467917256779">"በስም"</string>
<string name="sort_date" msgid="586080032956151448">"በተለወጠበት ቀን"</string>
<string name="sort_size" msgid="3350681319735474741">"በመጠን"</string>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index bf464b6..c7c9ef2 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"تحديد"</string>
<string name="button_copy" msgid="8706475544635021302">"نسخ"</string>
<string name="button_move" msgid="2202666023104202232">"نقل"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"إزالة"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"بحسب الاسم"</string>
<string name="sort_date" msgid="586080032956151448">"بحسب تاريخ التعديل"</string>
<string name="sort_size" msgid="3350681319735474741">"بحسب الحجم"</string>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index 0bd01a5..8c79ceb 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Seçin"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
<string name="button_move" msgid="2202666023104202232">"Köçürün"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Rədd edin"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Ad üzrə"</string>
<string name="sort_date" msgid="586080032956151448">"Tarix üzrə dəyişmiş"</string>
<string name="sort_size" msgid="3350681319735474741">"Ölçü üzrə"</string>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index 669145e..fdf57d0 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Избиране"</string>
<string name="button_copy" msgid="8706475544635021302">"Копиране"</string>
<string name="button_move" msgid="2202666023104202232">"Преместване"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Отхвърляне"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"По име"</string>
<string name="sort_date" msgid="586080032956151448">"По дата на промяната"</string>
<string name="sort_size" msgid="3350681319735474741">"По размер"</string>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 240ba6c..7caf57f 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"নির্বাচন করুন"</string>
<string name="button_copy" msgid="8706475544635021302">"অনুলিপি করুন"</string>
<string name="button_move" msgid="2202666023104202232">"সরান"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"খারিজ করুন"</string>
+ <string name="button_retry" msgid="4392027584153752797">"আবার চেষ্টা করুন"</string>
<string name="sort_name" msgid="9183560467917256779">"নামের দ্বারা"</string>
<string name="sort_date" msgid="586080032956151448">"পরিবর্তনের তারিখ দ্বারা"</string>
<string name="sort_size" msgid="3350681319735474741">"আকার অনুযায়ী"</string>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index 30accda..ab365ce 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Selecciona"</string>
<string name="button_copy" msgid="8706475544635021302">"Copia"</string>
<string name="button_move" msgid="2202666023104202232">"Desplaça"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignora"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Per nom"</string>
<string name="sort_date" msgid="586080032956151448">"Per data de modificació"</string>
<string name="sort_size" msgid="3350681319735474741">"Per mida"</string>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index cb1d973..62b313c 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Vybrat"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopírovat"</string>
<string name="button_move" msgid="2202666023104202232">"Přesunout"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Zavřít"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Zkusit znovu"</string>
<string name="sort_name" msgid="9183560467917256779">"Podle názvu"</string>
<string name="sort_date" msgid="586080032956151448">"Podle data úpravy"</string>
<string name="sort_size" msgid="3350681319735474741">"Podle velikosti"</string>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index f12737c..c3d7cdd 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Vælg"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiér"</string>
<string name="button_move" msgid="2202666023104202232">"Flyt"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Luk"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Prøv igen"</string>
<string name="sort_name" msgid="9183560467917256779">"Efter navn"</string>
<string name="sort_date" msgid="586080032956151448">"Efter ændringsdato"</string>
<string name="sort_size" msgid="3350681319735474741">"Efter størrelse"</string>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index a7be4db..f88b5c4 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Auswählen"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopieren"</string>
<string name="button_move" msgid="2202666023104202232">"Verschieben"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Schließen"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Erneut versuchen"</string>
<string name="sort_name" msgid="9183560467917256779">"Nach Name"</string>
<string name="sort_date" msgid="586080032956151448">"Nach Änderungsdatum"</string>
<string name="sort_size" msgid="3350681319735474741">"Nach Größe"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index 82155a8..dcca46c 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Επιλογή"</string>
<string name="button_copy" msgid="8706475544635021302">"Αντιγραφή"</string>
<string name="button_move" msgid="2202666023104202232">"Μετακίνηση"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Παράβλεψη"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Δοκιμάστε ξανά"</string>
<string name="sort_name" msgid="9183560467917256779">"Κατά όνομα"</string>
<string name="sort_date" msgid="586080032956151448">"Κατά ημερομηνία τροποποίησης"</string>
<string name="sort_size" msgid="3350681319735474741">"Κατά μέγεθος"</string>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index c609047..26fd30f 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"select"</string>
<string name="button_copy" msgid="8706475544635021302">"Copy"</string>
<string name="button_move" msgid="2202666023104202232">"Move"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dismiss"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Try again"</string>
<string name="sort_name" msgid="9183560467917256779">"By name"</string>
<string name="sort_date" msgid="586080032956151448">"By date modified"</string>
<string name="sort_size" msgid="3350681319735474741">"By size"</string>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index c609047..26fd30f 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"select"</string>
<string name="button_copy" msgid="8706475544635021302">"Copy"</string>
<string name="button_move" msgid="2202666023104202232">"Move"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dismiss"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Try again"</string>
<string name="sort_name" msgid="9183560467917256779">"By name"</string>
<string name="sort_date" msgid="586080032956151448">"By date modified"</string>
<string name="sort_size" msgid="3350681319735474741">"By size"</string>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index c609047..26fd30f 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"select"</string>
<string name="button_copy" msgid="8706475544635021302">"Copy"</string>
<string name="button_move" msgid="2202666023104202232">"Move"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dismiss"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Try again"</string>
<string name="sort_name" msgid="9183560467917256779">"By name"</string>
<string name="sort_date" msgid="586080032956151448">"By date modified"</string>
<string name="sort_size" msgid="3350681319735474741">"By size"</string>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 5936d83..6e805b9 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Seleccionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Descartar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Reintentar"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nombre"</string>
<string name="sort_date" msgid="586080032956151448">"Por fecha de modificación"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 2ed67dd..8fa1d2e 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Seleccionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Reintentar"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nombre"</string>
<string name="sort_date" msgid="586080032956151448">"Por fecha de modificación"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index e32936f..7008885 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Vali"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopeeri"</string>
<string name="button_move" msgid="2202666023104202232">"Teisalda"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Loobu"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Nime järgi"</string>
<string name="sort_date" msgid="586080032956151448">"Muutmiskuupäeva järgi"</string>
<string name="sort_size" msgid="3350681319735474741">"Suuruse järgi"</string>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index a1b6fc2..17d7c66 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Hautatu"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiatu"</string>
<string name="button_move" msgid="2202666023104202232">"Mugitu"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Baztertu"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Izenaren arabera"</string>
<string name="sort_date" msgid="586080032956151448">"Aldatze-dataren arabera"</string>
<string name="sort_size" msgid="3350681319735474741">"Tamainaren arabera"</string>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index bbbfe52..c4f6d58 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -22,7 +22,7 @@
<string name="title_save" msgid="2433679664882857999">"ذخیره در"</string>
<string name="menu_create_dir" msgid="5947289605844398389">"ایجاد پوشه"</string>
<string name="menu_grid" msgid="6878021334497835259">"نمای جدولی"</string>
- <string name="menu_list" msgid="7279285939892417279">"نمای فهرستوار"</string>
+ <string name="menu_list" msgid="7279285939892417279">"نمای فهرستی"</string>
<string name="menu_sort" msgid="7677740407158414452">"مرتبسازی براساس"</string>
<string name="menu_search" msgid="3816712084502856974">"جستجو"</string>
<string name="menu_settings" msgid="6008033148948428823">"تنظیمات"</string>
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"انتخاب"</string>
<string name="button_copy" msgid="8706475544635021302">"کپی"</string>
<string name="button_move" msgid="2202666023104202232">"انتقال"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"نپذیرفتن"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"براساس نام"</string>
<string name="sort_date" msgid="586080032956151448">"براساس تاریخ اصلاح"</string>
<string name="sort_size" msgid="3350681319735474741">"براساس اندازه"</string>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index fd289a7..3e87300 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Valitse"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopioi"</string>
<string name="button_move" msgid="2202666023104202232">"Siirrä"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Hylkää"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Nimen mukaan"</string>
<string name="sort_date" msgid="586080032956151448">"Muokkauspäivän mukaan"</string>
<string name="sort_size" msgid="3350681319735474741">"Koon mukaan"</string>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 342fc97..3e82f34 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Sélectionner"</string>
<string name="button_copy" msgid="8706475544635021302">"Copier"</string>
<string name="button_move" msgid="2202666023104202232">"Déplacer"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Faire disparaître"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
<string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
<string name="sort_size" msgid="3350681319735474741">"Par taille"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 7434e2f..0512ab1 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Sélectionner"</string>
<string name="button_copy" msgid="8706475544635021302">"Copier"</string>
<string name="button_move" msgid="2202666023104202232">"Déplacer"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Fermer"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Réessayer"</string>
<string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
<string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
<string name="sort_size" msgid="3350681319735474741">"Par taille"</string>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index b74018d..a1cd614 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Seleccionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificación"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 58384c9..059fb2c 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"પસંદ કરો"</string>
<string name="button_copy" msgid="8706475544635021302">"કૉપિ કરો"</string>
<string name="button_move" msgid="2202666023104202232">"ખસેડો"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"છોડી દો"</string>
+ <string name="button_retry" msgid="4392027584153752797">"ફરીથી પ્રયત્ન કરો"</string>
<string name="sort_name" msgid="9183560467917256779">"નામ દ્વારા"</string>
<string name="sort_date" msgid="586080032956151448">"સંશોધન તારીખ દ્વારા"</string>
<string name="sort_size" msgid="3350681319735474741">"કદ દ્વારા"</string>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index b42c69c..8e33a00 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"चुनें"</string>
<string name="button_copy" msgid="8706475544635021302">"कॉपी करें"</string>
<string name="button_move" msgid="2202666023104202232">"ले जाएं"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ख़ारिज करें"</string>
+ <string name="button_retry" msgid="4392027584153752797">"पुनः प्रयास करें"</string>
<string name="sort_name" msgid="9183560467917256779">"नाम के अनुसार"</string>
<string name="sort_date" msgid="586080032956151448">"बदलाव के दिनांक के अनुसार"</string>
<string name="sort_size" msgid="3350681319735474741">"आकार के अनुसार"</string>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 575c7ff..4774753 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Odaberi"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
<string name="button_move" msgid="2202666023104202232">"Premjesti"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Odbaci"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Po nazivu"</string>
<string name="sort_date" msgid="586080032956151448">"Po datumu izmjene"</string>
<string name="sort_size" msgid="3350681319735474741">"Po veličini"</string>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index 2d2e3c8..7a521ec 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Kiválasztás"</string>
<string name="button_copy" msgid="8706475544635021302">"Másolás"</string>
<string name="button_move" msgid="2202666023104202232">"Áthelyezés"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Elvetés"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Név szerint"</string>
<string name="sort_date" msgid="586080032956151448">"Módosítás dátuma szerint"</string>
<string name="sort_size" msgid="3350681319735474741">"Méret szerint"</string>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 0a4e7a3..95d73f0 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Ընտրել"</string>
<string name="button_copy" msgid="8706475544635021302">"Պատճենել"</string>
<string name="button_move" msgid="2202666023104202232">"Տեղափոխել"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Փակել"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Ըստ անվան"</string>
<string name="sort_date" msgid="586080032956151448">"Ըստ փոփոխման ամսաթվի"</string>
<string name="sort_size" msgid="3350681319735474741">"Ըստ չափի"</string>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 59aadf1..63d415c 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Pilih"</string>
<string name="button_copy" msgid="8706475544635021302">"Salin"</string>
<string name="button_move" msgid="2202666023104202232">"Pindahkan"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Tutup"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Coba Lagi"</string>
<string name="sort_name" msgid="9183560467917256779">"Menurut nama"</string>
<string name="sort_date" msgid="586080032956151448">"Menurut tanggal diubah"</string>
<string name="sort_size" msgid="3350681319735474741">"Menurut ukuran"</string>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index a0f4987..0c0e47f 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Velja"</string>
<string name="button_copy" msgid="8706475544635021302">"Afrita"</string>
<string name="button_move" msgid="2202666023104202232">"Færa"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Hunsa"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Reyna aftur"</string>
<string name="sort_name" msgid="9183560467917256779">"Eftir heiti"</string>
<string name="sort_date" msgid="586080032956151448">"Eftir breytingadags."</string>
<string name="sort_size" msgid="3350681319735474741">"Eftir stærð"</string>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index ce837da..de129a7 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Seleziona"</string>
<string name="button_copy" msgid="8706475544635021302">"Copia"</string>
<string name="button_move" msgid="2202666023104202232">"Sposta"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignora"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Riprova"</string>
<string name="sort_name" msgid="9183560467917256779">"Per nome"</string>
<string name="sort_date" msgid="586080032956151448">"Per data di modifica"</string>
<string name="sort_size" msgid="3350681319735474741">"Per dimensioni"</string>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index 89e6403..dbe4630 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"בחר"</string>
<string name="button_copy" msgid="8706475544635021302">"העתק"</string>
<string name="button_move" msgid="2202666023104202232">"העבר"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"הסר"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"לפי שם"</string>
<string name="sort_date" msgid="586080032956151448">"לפי תאריך שינוי"</string>
<string name="sort_size" msgid="3350681319735474741">"לפי גודל"</string>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 92e1023..45f0ea1 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"選択"</string>
<string name="button_copy" msgid="8706475544635021302">"コピー"</string>
<string name="button_move" msgid="2202666023104202232">"移動"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"表示しない"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"名前順"</string>
<string name="sort_date" msgid="586080032956151448">"更新日順"</string>
<string name="sort_size" msgid="3350681319735474741">"サイズ順"</string>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index 9a324ba..114e73c 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"არჩევა"</string>
<string name="button_copy" msgid="8706475544635021302">"კოპირება"</string>
<string name="button_move" msgid="2202666023104202232">"გადაადგილება"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"დახურვა"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"სახელით"</string>
<string name="sort_date" msgid="586080032956151448">"ცვლილების თარიღით"</string>
<string name="sort_size" msgid="3350681319735474741">"ზომით"</string>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index 66f69f5..af123c6 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Таңдау"</string>
<string name="button_copy" msgid="8706475544635021302">"Көшіру"</string>
<string name="button_move" msgid="2202666023104202232">"Жылжыту"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Өшіру"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Атауы бойынша"</string>
<string name="sort_date" msgid="586080032956151448">"Өзгертілген мерзімі бойынша"</string>
<string name="sort_size" msgid="3350681319735474741">"Өлшемі бойынша"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 5249d97..8f3feae 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"ជ្រើស"</string>
<string name="button_copy" msgid="8706475544635021302">"ចម្លង"</string>
<string name="button_move" msgid="2202666023104202232">"ផ្លាស់ទី"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"បដិសេធ"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"តាមឈ្មោះ"</string>
<string name="sort_date" msgid="586080032956151448">"តាមកាលបរិច្ឆេទបានកែប្រែ"</string>
<string name="sort_size" msgid="3350681319735474741">"តាមទំហំ"</string>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index d5eda84..826cf1e 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"ಆಯ್ಕೆಮಾಡು"</string>
<string name="button_copy" msgid="8706475544635021302">"ನಕಲಿಸು"</string>
<string name="button_move" msgid="2202666023104202232">"ಸರಿಸು"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ವಜಾಗೊಳಿಸಿ"</string>
+ <string name="button_retry" msgid="4392027584153752797">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="sort_name" msgid="9183560467917256779">"ಹೆಸರಿನ ಪ್ರಕಾರ"</string>
<string name="sort_date" msgid="586080032956151448">"ಮಾರ್ಪಡಿಸಿರುವ ದಿನಾಂಕದ ಪ್ರಕಾರ"</string>
<string name="sort_size" msgid="3350681319735474741">"ಗಾತ್ರದ ಪ್ರಕಾರ"</string>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index 77a0df6..322b43b 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"선택"</string>
<string name="button_copy" msgid="8706475544635021302">"복사"</string>
<string name="button_move" msgid="2202666023104202232">"이동"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"닫기"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"이름순"</string>
<string name="sort_date" msgid="586080032956151448">"수정된 날짜순"</string>
<string name="sort_size" msgid="3350681319735474741">"크기순"</string>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index 14a6331..c8aa69e 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Тандоо"</string>
<string name="button_copy" msgid="8706475544635021302">"Көчүрүү"</string>
<string name="button_move" msgid="2202666023104202232">"Жылдыруу"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Этибарга албоо"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Аты боюнча"</string>
<string name="sort_date" msgid="586080032956151448">"Өзгөртүлгөн күнү боюнча"</string>
<string name="sort_size" msgid="3350681319735474741">"Өлчөмү боюнча"</string>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index 2d099f4..dedfa05 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"ເລືອກ"</string>
<string name="button_copy" msgid="8706475544635021302">"ສຳເນົາ"</string>
<string name="button_move" msgid="2202666023104202232">"ຍ້າຍ"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ປິດໄວ້"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"ຕາມຊື່"</string>
<string name="sort_date" msgid="586080032956151448">"ຕາມວັນທີທີ່ແກ້ໄຂ"</string>
<string name="sort_size" msgid="3350681319735474741">"ຕາມຂະໜາດ"</string>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 7b458add..0c8b443 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Pasirinkti"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopijuoti"</string>
<string name="button_move" msgid="2202666023104202232">"Perkelti"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Atsisakyti"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Pagal pavadinimą"</string>
<string name="sort_date" msgid="586080032956151448">"Pagal keitimo datą"</string>
<string name="sort_size" msgid="3350681319735474741">"Pagal dydį"</string>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index 44909ce..5c79554 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Atlasīt"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopēt"</string>
<string name="button_move" msgid="2202666023104202232">"Pārvietot"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Noraidīt"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Pēc nosaukuma"</string>
<string name="sort_date" msgid="586080032956151448">"Pēc pārveidošanas datuma"</string>
<string name="sort_size" msgid="3350681319735474741">"Pēc lieluma"</string>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index 7408d0b..b2cb9f1 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Избери"</string>
<string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
<string name="button_move" msgid="2202666023104202232">"Премести"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Отфрли"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"По име"</string>
<string name="sort_date" msgid="586080032956151448">"Изменети по датум"</string>
<string name="sort_size" msgid="3350681319735474741">"По големина"</string>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index af21db9..07efa66 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"തിരഞ്ഞെടുക്കുക"</string>
<string name="button_copy" msgid="8706475544635021302">"പകര്ത്തുക"</string>
<string name="button_move" msgid="2202666023104202232">"നീക്കുക"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ഡിസ്മിസ് ചെയ്യുക"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"പേര് പ്രകാരം"</string>
<string name="sort_date" msgid="586080032956151448">"പരിഷ്ക്കരിച്ച തീയതി പ്രകാരം"</string>
<string name="sort_size" msgid="3350681319735474741">"വലുപ്പം പ്രകാരം"</string>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index 1475e08..d45778c 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Сонгох"</string>
<string name="button_copy" msgid="8706475544635021302">"Хуулах"</string>
<string name="button_move" msgid="2202666023104202232">"Зөөх"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Алгасах"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Нэрээр"</string>
<string name="sort_date" msgid="586080032956151448">"Өөрчлөгдсөн огноогоор"</string>
<string name="sort_size" msgid="3350681319735474741">"Хэмжээгээр"</string>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index b7654881..8d70143 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"निवडा"</string>
<string name="button_copy" msgid="8706475544635021302">"कॉपी करा"</string>
<string name="button_move" msgid="2202666023104202232">"हलवा"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"डिसमिस करा"</string>
+ <string name="button_retry" msgid="4392027584153752797">"पुन्हा प्रयत्न करा"</string>
<string name="sort_name" msgid="9183560467917256779">"नावानुसार"</string>
<string name="sort_date" msgid="586080032956151448">"सुधारित केलेल्या तारखेनुसार"</string>
<string name="sort_size" msgid="3350681319735474741">"आकारानुसार"</string>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 4682957..2250109 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Pilih"</string>
<string name="button_copy" msgid="8706475544635021302">"Salin"</string>
<string name="button_move" msgid="2202666023104202232">"Alihkan"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Tolak"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Mengikut nama"</string>
<string name="sort_date" msgid="586080032956151448">"Mengikut tarikh diubah"</string>
<string name="sort_size" msgid="3350681319735474741">"Mengikut saiz"</string>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index a422898..480c27b 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"ရွေးရန်"</string>
<string name="button_copy" msgid="8706475544635021302">"ကူးယူရန်"</string>
<string name="button_move" msgid="2202666023104202232">"ရွေ့မည်"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ပယ်ရန်"</string>
+ <string name="button_retry" msgid="4392027584153752797">"ထပ် စမ်းကြည့်ပါ"</string>
<string name="sort_name" msgid="9183560467917256779">"အမည်ဖြင့်"</string>
<string name="sort_date" msgid="586080032956151448">"ပြင်ဆင်မှု ရက်စွဲဖြင့်"</string>
<string name="sort_size" msgid="3350681319735474741">"အရွယ်အစားဖြင့်"</string>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index 5bcc50f..48355aa 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Velg"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiér"</string>
<string name="button_move" msgid="2202666023104202232">"Flytt"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Avvis"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Prøv på nytt"</string>
<string name="sort_name" msgid="9183560467917256779">"Etter navn"</string>
<string name="sort_date" msgid="586080032956151448">"Etter endringsdato"</string>
<string name="sort_size" msgid="3350681319735474741">"Etter størrelse"</string>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index b121035..53942c1 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"चयन गर्नुहोस्"</string>
<string name="button_copy" msgid="8706475544635021302">"प्रतिलिपि बनाउनुहोस्"</string>
<string name="button_move" msgid="2202666023104202232">"सार्नुहोस्"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"खारेज गर्नुहोस्"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"नाम अनुसार"</string>
<string name="sort_date" msgid="586080032956151448">"परिमार्जित मिति अनुसार"</string>
<string name="sort_size" msgid="3350681319735474741">"आकार अनुसार"</string>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index 88ef0bf..d3e6bbe 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selecteren"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiëren"</string>
<string name="button_move" msgid="2202666023104202232">"Verplaatsen"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Sluiten"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Opnieuw proberen"</string>
<string name="sort_name" msgid="9183560467917256779">"Op naam"</string>
<string name="sort_date" msgid="586080032956151448">"Op aanpassingsdatum"</string>
<string name="sort_size" msgid="3350681319735474741">"Op grootte"</string>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index a55fc27..3b14396 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"ਚੁਣੋ"</string>
<string name="button_copy" msgid="8706475544635021302">"ਕਾਪੀ ਕਰੋ"</string>
<string name="button_move" msgid="2202666023104202232">"ਮੂਵ ਕਰੋ"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ਬਰਖਾਸਤ ਕਰੋ"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"ਨਾਮ ਮੁਤਾਬਕ"</string>
<string name="sort_date" msgid="586080032956151448">"ਤਾਰੀਖ ਮੁਤਾਬਕ ਸੰਸ਼ੋਧਿਤ"</string>
<string name="sort_size" msgid="3350681319735474741">"ਆਕਾਰ ਮੁਤਾਬਕ"</string>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 0099e5a..3e4ef68 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Wybierz"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiuj"</string>
<string name="button_move" msgid="2202666023104202232">"Przenieś"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Zamknij"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Według nazwy"</string>
<string name="sort_date" msgid="586080032956151448">"Według daty edycji"</string>
<string name="sort_size" msgid="3350681319735474741">"Według rozmiaru"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index 18506dc..ca984cf 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selecionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dispensar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Tentar novamente"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamanho"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index adf6ec9..ab67358 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selecionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Tentar novamente"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamanho"</string>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index 18506dc..ca984cf 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selecionar"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Dispensar"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Tentar novamente"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamanho"</string>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index 1b046a3..e927b78 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Selectați"</string>
<string name="button_copy" msgid="8706475544635021302">"Copiați"</string>
<string name="button_move" msgid="2202666023104202232">"Mutați"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Închideți"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Încercați din nou"</string>
<string name="sort_name" msgid="9183560467917256779">"După nume"</string>
<string name="sort_date" msgid="586080032956151448">"După data modificării"</string>
<string name="sort_size" msgid="3350681319735474741">"După dimensiune"</string>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index 84ae160..cdf20ca 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Выбрать"</string>
<string name="button_copy" msgid="8706475544635021302">"Копировать"</string>
<string name="button_move" msgid="2202666023104202232">"Переместить"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Скрыть"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"По названию"</string>
<string name="sort_date" msgid="586080032956151448">"По дате изменения"</string>
<string name="sort_size" msgid="3350681319735474741">"По размеру"</string>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index d9d5818..138f8a6 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"තෝරන්න"</string>
<string name="button_copy" msgid="8706475544635021302">"පිටපත් කිරීම"</string>
<string name="button_move" msgid="2202666023104202232">"ගෙන යන්න"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ඉවතලන්න"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"නමින්"</string>
<string name="sort_date" msgid="586080032956151448">"වෙනස් කරන ලද දිනයෙන්"</string>
<string name="sort_size" msgid="3350681319735474741">"ප්රමාණය මගින්"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index e0ff5fc..4310819 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Vybrať"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopírovať"</string>
<string name="button_move" msgid="2202666023104202232">"Presunúť"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Odmietnuť"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Podľa názvu"</string>
<string name="sort_date" msgid="586080032956151448">"Podľa dátumu zmeny"</string>
<string name="sort_size" msgid="3350681319735474741">"Podľa veľkosti"</string>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index 83e5124..c9982a7 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Izberi"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
<string name="button_move" msgid="2202666023104202232">"Premik"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Opusti"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Po imenu"</string>
<string name="sort_date" msgid="586080032956151448">"Po datumu spremembe"</string>
<string name="sort_size" msgid="3350681319735474741">"Po velikosti"</string>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index 981daf2..f1ee1bc 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Zgjidh"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopjo"</string>
<string name="button_move" msgid="2202666023104202232">"Zhvendos"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Largoje"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Sipas emrit"</string>
<string name="sort_date" msgid="586080032956151448">"Sipas datës së modifikimit"</string>
<string name="sort_size" msgid="3350681319735474741">"Sipas madhësisë"</string>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index bd0e9af..c5116c2 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Изабери"</string>
<string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
<string name="button_move" msgid="2202666023104202232">"Премести"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Одбаци"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Према имену"</string>
<string name="sort_date" msgid="586080032956151448">"Према датуму измене"</string>
<string name="sort_size" msgid="3350681319735474741">"Према величини"</string>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index 2569e00..06e6514 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Välj"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopiera"</string>
<string name="button_move" msgid="2202666023104202232">"Flytta"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ta bort permanent"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Försök igen"</string>
<string name="sort_name" msgid="9183560467917256779">"Efter namn"</string>
<string name="sort_date" msgid="586080032956151448">"Efter ändringsdatum"</string>
<string name="sort_size" msgid="3350681319735474741">"Efter storlek"</string>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index ce186d7..79bae1f 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Teua"</string>
<string name="button_copy" msgid="8706475544635021302">"Nakili"</string>
<string name="button_move" msgid="2202666023104202232">"Hamisha"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Ondoa"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Jaribu Tena"</string>
<string name="sort_name" msgid="9183560467917256779">"Kwa jina"</string>
<string name="sort_date" msgid="586080032956151448">"Kwa tarehe viliporekebishwa"</string>
<string name="sort_size" msgid="3350681319735474741">"Kwa ukubwa"</string>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index b7b9c09..117aabc 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"தேர்ந்தெடு"</string>
<string name="button_copy" msgid="8706475544635021302">"நகலெடு"</string>
<string name="button_move" msgid="2202666023104202232">"நகர்த்து"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"நிராகரி"</string>
+ <string name="button_retry" msgid="4392027584153752797">"மீண்டும் முயற்சிக்கவும்"</string>
<string name="sort_name" msgid="9183560467917256779">"பெயரின்படி"</string>
<string name="sort_date" msgid="586080032956151448">"திருத்தப்பட்ட தேதியின்படி"</string>
<string name="sort_size" msgid="3350681319735474741">"அளவின்படி"</string>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index 7a81486..21b7f58 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"ఎంచుకోండి"</string>
<string name="button_copy" msgid="8706475544635021302">"కాపీ చేయి"</string>
<string name="button_move" msgid="2202666023104202232">"తరలించు"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"తీసివేయి"</string>
+ <string name="button_retry" msgid="4392027584153752797">"మళ్లీ ప్రయత్నించు"</string>
<string name="sort_name" msgid="9183560467917256779">"పేరు ద్వారా"</string>
<string name="sort_date" msgid="586080032956151448">"సవరించిన తేదీ ద్వారా"</string>
<string name="sort_size" msgid="3350681319735474741">"పరిమాణం ద్వారా"</string>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 87c0a73..8a49708 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"เลือก"</string>
<string name="button_copy" msgid="8706475544635021302">"คัดลอก"</string>
<string name="button_move" msgid="2202666023104202232">"ย้าย"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"ปิด"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"ตามชื่อ"</string>
<string name="sort_date" msgid="586080032956151448">"ตามวันที่ที่ปรับเปลี่ยน"</string>
<string name="sort_size" msgid="3350681319735474741">"ตามขนาด"</string>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index eaef936..5c0dc12 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Pumili"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopyahin"</string>
<string name="button_move" msgid="2202666023104202232">"Ilipat"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"I-dismiss"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Ayon sa pangalan"</string>
<string name="sort_date" msgid="586080032956151448">"Ayon sa petsa ng pagbago"</string>
<string name="sort_size" msgid="3350681319735474741">"Ayon sa laki"</string>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index 8c0596f..092b38e 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Seç"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
<string name="button_move" msgid="2202666023104202232">"Taşı"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Kapat"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Ada göre"</string>
<string name="sort_date" msgid="586080032956151448">"Değişiklik tarihine göre"</string>
<string name="sort_size" msgid="3350681319735474741">"Boyuta göre"</string>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index 9bbc59a..dff37c3 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Вибрати"</string>
<string name="button_copy" msgid="8706475544635021302">"Копіювати"</string>
<string name="button_move" msgid="2202666023104202232">"Перемістити"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Закрити"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Повторити спробу"</string>
<string name="sort_name" msgid="9183560467917256779">"За назвою"</string>
<string name="sort_date" msgid="586080032956151448">"За датою змінення"</string>
<string name="sort_size" msgid="3350681319735474741">"За розміром"</string>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 0c12aa1..82822ec 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"منتخب کریں"</string>
<string name="button_copy" msgid="8706475544635021302">"کاپی کریں"</string>
<string name="button_move" msgid="2202666023104202232">"منتقل کریں"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"برخاست کریں"</string>
+ <string name="button_retry" msgid="4392027584153752797">"دوبارہ کوشش کریں"</string>
<string name="sort_name" msgid="9183560467917256779">"نام کے لحاظ سے"</string>
<string name="sort_date" msgid="586080032956151448">"ترمیم کی تاریخ کے لحاظ سے"</string>
<string name="sort_size" msgid="3350681319735474741">"سائز کے لحاظ سے"</string>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index ec91885..d0cfacc 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -24,7 +24,7 @@
<string name="menu_grid" msgid="6878021334497835259">"Katak ko‘rinishida"</string>
<string name="menu_list" msgid="7279285939892417279">"Ro‘yxat ko‘rinishida"</string>
<string name="menu_sort" msgid="7677740407158414452">"Saralash"</string>
- <string name="menu_search" msgid="3816712084502856974">"Izlash"</string>
+ <string name="menu_search" msgid="3816712084502856974">"Qidirish"</string>
<string name="menu_settings" msgid="6008033148948428823">"Sozlamalar"</string>
<string name="menu_open" msgid="432922957274920903">"Ochish"</string>
<string name="menu_save" msgid="2394743337684426338">"Saqlash"</string>
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Tanlash"</string>
<string name="button_copy" msgid="8706475544635021302">"Nusxalash"</string>
<string name="button_move" msgid="2202666023104202232">"Ko‘chirib o‘tkazish"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"O‘chirish"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Nomi bo‘yicha"</string>
<string name="sort_date" msgid="586080032956151448">"Tahrir sanasi bo‘yicha"</string>
<string name="sort_size" msgid="3350681319735474741">"Hajmi bo‘yicha"</string>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index a652ecc..4583362 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"Chọn"</string>
<string name="button_copy" msgid="8706475544635021302">"Sao chép"</string>
<string name="button_move" msgid="2202666023104202232">"Di chuyển"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Loại bỏ"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"Theo tên"</string>
<string name="sort_date" msgid="586080032956151448">"Theo ngày sửa đổi"</string>
<string name="sort_size" msgid="3350681319735474741">"Theo kích thước"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index b0f5480..d577e14 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"选择"</string>
<string name="button_copy" msgid="8706475544635021302">"复制"</string>
<string name="button_move" msgid="2202666023104202232">"移动"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"关闭"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"按名称"</string>
<string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
<string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index 220b716..3d44047 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"選取"</string>
<string name="button_copy" msgid="8706475544635021302">"複製"</string>
<string name="button_move" msgid="2202666023104202232">"移動"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"按名稱"</string>
<string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
<string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index 9c21aa5..aaad98c 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -44,6 +44,9 @@
<string name="button_select" msgid="527196987259139214">"選取"</string>
<string name="button_copy" msgid="8706475544635021302">"複製"</string>
<string name="button_move" msgid="2202666023104202232">"移動"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
+ <!-- no translation found for button_retry (4392027584153752797) -->
+ <skip />
<string name="sort_name" msgid="9183560467917256779">"依名稱"</string>
<string name="sort_date" msgid="586080032956151448">"依修改日期"</string>
<string name="sort_size" msgid="3350681319735474741">"依大小"</string>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index 5ce9f12..8f20cc4 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -44,6 +44,8 @@
<string name="button_select" msgid="527196987259139214">"Khetha"</string>
<string name="button_copy" msgid="8706475544635021302">"Kopisha"</string>
<string name="button_move" msgid="2202666023104202232">"Hambisa"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Cashisa"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Zama futhi"</string>
<string name="sort_name" msgid="9183560467917256779">"Ngegama"</string>
<string name="sort_date" msgid="586080032956151448">"Ngedethi yokuguqula"</string>
<string name="sort_size" msgid="3350681319735474741">"Ngosayizi"</string>
diff --git a/core/res/res/color/btn_colored_material.xml b/packages/DocumentsUI/res/values/attrs.xml
similarity index 64%
copy from core/res/res/color/btn_colored_material.xml
copy to packages/DocumentsUI/res/values/attrs.xml
index b45f824..0afc3a2 100644
--- a/core/res/res/color/btn_colored_material.xml
+++ b/packages/DocumentsUI/res/values/attrs.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?attr/disabledAlpha"
- android:color="?attr/colorButtonNormal" />
- <item android:color="?attr/colorAccent" />
-</selector>
+<resources>
+ <declare-styleable name="DocumentsBaseTheme">
+ <attr name="colorActionMode" format="color"/>
+ </declare-styleable>
+</resources>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 6002dde..cb6957d 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -19,18 +19,20 @@
<color name="material_grey_300">#ffeeeeee</color>
<color name="material_grey_600">#ff757575</color>
<color name="material_grey_800">#ff424242</color>
- <color name="material_blue_700">#ff1976d2</color>
- <color name="material_blue_500">#ff2196f3</color>
<color name="primary_dark">@*android:color/material_blue_grey_900</color>
<color name="primary">@*android:color/material_blue_grey_800</color>
<color name="accent">@*android:color/material_deep_teal_500</color>
+ <color name="platform_blue_100">#ffd0d9ff</color>
+ <color name="platform_blue_500">#ff5677fc</color>
+ <color name="platform_blue_700">#ff455ede</color>
+ <color name="platform_blue_a100">#ffa6baff</color>
+ <color name="platform_blue_a200">#ffff5177</color>
+
<color name="directory_background">@color/material_grey_300</color>
<color name="item_doc_grid_background">#FFFFFFFF</color>
<color name="item_doc_grid_protect_background">#88000000</color>
- <color name="status_bar_background">@color/material_blue_700</color>
- <color name="action_mode_status_bar_background">@color/material_grey_800</color>
<color name="band_select_background">#88ffffff</color>
<color name="band_select_border">#44000000</color>
</resources>
diff --git a/packages/DocumentsUI/res/values/layouts.xml b/packages/DocumentsUI/res/values/layouts.xml
index c73a1cb..8ac1ac2 100644
--- a/packages/DocumentsUI/res/values/layouts.xml
+++ b/packages/DocumentsUI/res/values/layouts.xml
@@ -17,4 +17,5 @@
<resources>
<item name="docs_activity" type="layout">@layout/drawer_layout</item>
<item name="files_activity" type="layout">@layout/drawer_layout</item>
+ <item name="manage_roots_activity" type="layout">@layout/single_pane_layout</item>
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 4b44d7c..a4acb60 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -54,6 +54,8 @@
<!-- Menu item title that moves the selected documents [CHAR LIMIT=24] -->
<string name="menu_move">Move to\u2026</string>
+ <!-- Menu item title that creates a new window in the activity [CHAR LIMIT=24] -->
+ <string name="menu_new_window">New window</string>
<!-- Menu item title that copies the selected documents to clipboard [CHAR LIMIT=24] -->
<string name="menu_copy_to_clipboard">Copy</string>
<!-- Menu item title that pastes files from the clipboard [CHAR LIMIT=24] -->
@@ -79,7 +81,10 @@
<string name="button_copy">Copy</string>
<!-- Button label that moves files to the current directory [CHAR LIMIT=24] -->
<string name="button_move">Move</string>
-
+ <!-- Button label that hides the error bar [CHAR LIMIT=24] -->
+ <string name="button_dismiss">Dismiss</string>
+ <string name="button_retry">Try Again</string>
+
<!-- Mode that sorts documents by their display name alphabetically [CHAR LIMIT=24] -->
<string name="sort_name">By name</string>
<!-- Mode that sorts documents by their last modified time in descending order; most recent first [CHAR LIMIT=24] -->
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index 22add98..c13f144 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -28,6 +28,7 @@
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorAccent">@color/accent</item>
+ <item name="colorActionMode">@color/material_grey_800</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
@@ -43,6 +44,7 @@
<item name="actionBarWidgetTheme">@null</item>
<item name="actionBarTheme">@style/ActionBarTheme</item>
<item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
+ <item name="colorActionMode">@color/material_grey_800</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
@@ -57,6 +59,10 @@
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorAccent">@color/accent</item>
+
+ <item name="android:actionModeStyle">@style/ActionModeStyle</item>
+
+ <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="ActionModeStyle" parent="@android:style/Widget.Material.Light.ActionMode">
@@ -64,16 +70,27 @@
</style>
<style name="AlertDialogTheme" parent="@android:style/Theme.Material.Light.Dialog.Alert">
- <item name="android:colorAccent">@color/material_blue_700</item>
+ <item name="android:colorAccent">@color/platform_blue_700</item>
</style>
<style name="FilesTheme" parent="@style/DocumentsBaseTheme.FullScreen">
- <item name="android:colorPrimaryDark">@color/material_blue_700</item>
- <item name="android:colorPrimary">@color/material_blue_500</item>
- <item name="android:colorAccent">@color/material_blue_700</item>
- <item name="android:actionModeStyle">@style/ActionModeStyle</item>
-
+ <item name="android:colorPrimaryDark">@color/platform_blue_700</item>
+ <item name="android:colorPrimary">@color/platform_blue_500</item>
+ <item name="android:colorAccent">@color/platform_blue_700</item>
+ <item name="colorControlActivated">@color/platform_blue_a100</item>
+ <item name="android:actionModeStyle">@style/FilesActionModeStyle</item>
+ <item name="colorActionMode">@color/platform_blue_700</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
+ <style name="FilesActionModeStyle" parent="@android:style/Widget.Material.Light.ActionMode">
+ <item name="android:background">@color/platform_blue_100</item>
+ </style>
+
+ <style name="TrimmedHorizontalProgressBar" parent="android:Widget.Material.ProgressBar.Horizontal">
+ <item name="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material_trimmed</item>
+ <item name="android:minHeight">3dp</item>
+ <item name="android:maxHeight">3dp</item>
+ </style>
+
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 2835106..a6a45e5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -33,21 +33,17 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.util.Log;
-import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnActionExpandListener;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewStub;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
@@ -68,7 +64,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executor;
@@ -80,12 +75,13 @@
RootsCache mRoots;
SearchManager mSearchManager;
DrawerController mDrawer;
+ boolean mProductivityDevice;
+ private final String mTag;
@LayoutRes
private int mLayoutId;
- private final String mTag;
+ private DirectoryContainerView mDirectoryContainer;
- public abstract State getDisplayState();
public abstract void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
@@ -93,7 +89,7 @@
abstract void onDirectoryChanged(int anim);
abstract void updateActionBar();
abstract void saveStackBlocking();
- abstract State buildDefaultState();
+ abstract State buildState();
public BaseActivity(@LayoutRes int layoutId, String tag) {
mLayoutId = layoutId;
@@ -104,13 +100,15 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ mProductivityDevice = getResources().getBoolean(R.bool.productivity_device);
mState = (icicle != null)
? icicle.<State>getParcelable(EXTRA_STATE)
- : buildDefaultState();
+ : buildState();
setContentView(mLayoutId);
mRoots = DocumentsApplication.getRootsCache(this);
+ mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
mSearchManager = new SearchManager();
// Base classes must update result in their onCreate.
@@ -118,22 +116,6 @@
}
@Override
- public void onResume() {
- super.onResume();
-
- final State state = getDisplayState();
- final RootInfo root = getCurrentRoot();
-
- // If we're browsing a specific root, and that root went away, then we
- // have no reason to hang around
- if (state.action == State.ACTION_BROWSE && root != null) {
- if (mRoots.getRootBlocking(root.authority, root.rootId) == null) {
- finish();
- }
- }
- }
-
- @Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean showMenu = super.onCreateOptionsMenu(menu);
@@ -154,30 +136,52 @@
final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
final MenuItem grid = menu.findItem(R.id.menu_grid);
final MenuItem list = menu.findItem(R.id.menu_list);
-
final MenuItem advanced = menu.findItem(R.id.menu_advanced);
final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
+ final MenuItem settings = menu.findItem(R.id.menu_settings);
mSearchManager.update(root);
// Search uses backend ranking; no sorting
sort.setVisible(cwd != null && !mSearchManager.isSearching());
- State state = getDisplayState();
- grid.setVisible(state.derivedMode != State.MODE_GRID);
- list.setVisible(state.derivedMode != State.MODE_LIST);
-
- // Only sort by size when visible
- sortSize.setVisible(state.showSize);
-
advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
? R.string.menu_advanced_hide : R.string.menu_advanced_show);
fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
? R.string.menu_file_size_hide : R.string.menu_file_size_show);
+ State state = getDisplayState();
+
+ sortSize.setVisible(state.showSize); // Only sort by size when visible
+ fileSize.setVisible(!state.showSize);
+ grid.setVisible(state.derivedMode != State.MODE_GRID);
+ list.setVisible(state.derivedMode != State.MODE_LIST);
+ advanced.setVisible(!mState.showAdvanced);
+ settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
+
return shown;
}
+ State buildDefaultState() {
+ State state = new State();
+
+ final Intent intent = getIntent();
+
+ state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+
+ state.forceSize = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_FILESIZE, false);
+ state.showSize = state.forceSize || LocalPreferences.getDisplayFileSize(this);
+
+ state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+ state.showAdvanced = state.forceAdvanced
+ || LocalPreferences.getDisplayAdvancedDevices(this);
+
+ state.initAcceptMimes(intent);
+ state.excludedAuthorities = getExcludedAuthorities();
+
+ return state;
+ }
+
void onStackRestored(boolean restored, boolean external) {}
void onRootPicked(RootInfo root) {
@@ -196,7 +200,7 @@
if (mRoots.isRecentsRoot(root)) {
onCurrentDirectoryChanged(ANIM_SIDE);
} else {
- new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
+ new PickRootTask(root).executeOnExecutor(getExecutorForCurrentDirectory());
}
}
@@ -206,6 +210,7 @@
switch (item.getItemId()) {
case R.id.menu_advanced:
case R.id.menu_file_size:
+ case R.id.menu_new_window:
break;
default:
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
@@ -295,6 +300,7 @@
* @param anim
*/
final void onCurrentDirectoryChanged(int anim) {
+ mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
onDirectoryChanged(anim);
final RootsFragment roots = RootsFragment.get(getFragmentManager());
@@ -344,6 +350,10 @@
return (BaseActivity) fragment.getActivity();
}
+ public State getDisplayState() {
+ return mState;
+ }
+
public static abstract class DocumentsIntent {
/** Intent action name to open copy destination. */
public static String ACTION_OPEN_COPY_DESTINATION =
@@ -356,118 +366,6 @@
public static String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
}
- public static class State implements android.os.Parcelable {
- public int action;
- public String[] acceptMimes;
-
- /** Explicit user choice */
- public int userMode = MODE_UNKNOWN;
- /** Derived after loader */
- public int derivedMode = MODE_LIST;
-
- /** Explicit user choice */
- public int userSortOrder = SORT_ORDER_UNKNOWN;
- /** Derived after loader */
- public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
-
- public boolean allowMultiple;
- public boolean showSize;
- public boolean localOnly ;
- public boolean forceAdvanced ;
- public boolean showAdvanced ;
- public boolean stackTouched ;
- public boolean restored ;
- public boolean directoryCopy ;
- /** Transfer mode for file copy/move operations. */
- public int transferMode;
-
- /** Current user navigation stack; empty implies recents. */
- public DocumentStack stack = new DocumentStack();
- /** Currently active search, overriding any stack. */
- public String currentSearch;
-
- /** Instance state for every shown directory */
- public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>();
-
- /** Currently copying file */
- public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<DocumentInfo>();
-
- /** Name of the package that started DocsUI */
- public List<String> excludedAuthorities = new ArrayList<>();
-
- public static final int ACTION_OPEN = 1;
- public static final int ACTION_CREATE = 2;
- public static final int ACTION_GET_CONTENT = 3;
- public static final int ACTION_OPEN_TREE = 4;
- public static final int ACTION_MANAGE = 5;
- public static final int ACTION_BROWSE = 6;
- public static final int ACTION_BROWSE_ALL = 7;
- public static final int ACTION_OPEN_COPY_DESTINATION = 8;
-
- public static final int MODE_UNKNOWN = 0;
- public static final int MODE_LIST = 1;
- public static final int MODE_GRID = 2;
-
- public static final int SORT_ORDER_UNKNOWN = 0;
- public static final int SORT_ORDER_DISPLAY_NAME = 1;
- public static final int SORT_ORDER_LAST_MODIFIED = 2;
- public static final int SORT_ORDER_SIZE = 3;
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(action);
- out.writeInt(userMode);
- out.writeStringArray(acceptMimes);
- out.writeInt(userSortOrder);
- out.writeInt(allowMultiple ? 1 : 0);
- out.writeInt(showSize ? 1 : 0);
- out.writeInt(localOnly ? 1 : 0);
- out.writeInt(forceAdvanced ? 1 : 0);
- out.writeInt(showAdvanced ? 1 : 0);
- out.writeInt(stackTouched ? 1 : 0);
- out.writeInt(restored ? 1 : 0);
- DurableUtils.writeToParcel(out, stack);
- out.writeString(currentSearch);
- out.writeMap(dirState);
- out.writeList(selectedDocumentsForCopy);
- out.writeList(excludedAuthorities);
- }
-
- public static final Creator<State> CREATOR = new Creator<State>() {
- @Override
- public State createFromParcel(Parcel in) {
- final State state = new State();
- state.action = in.readInt();
- state.userMode = in.readInt();
- state.acceptMimes = in.readStringArray();
- state.userSortOrder = in.readInt();
- state.allowMultiple = in.readInt() != 0;
- state.showSize = in.readInt() != 0;
- state.localOnly = in.readInt() != 0;
- state.forceAdvanced = in.readInt() != 0;
- state.showAdvanced = in.readInt() != 0;
- state.stackTouched = in.readInt() != 0;
- state.restored = in.readInt() != 0;
- DurableUtils.readFromParcel(in, state.stack);
- state.currentSearch = in.readString();
- in.readMap(state.dirState, null);
- in.readList(state.selectedDocumentsForCopy, null);
- in.readList(state.excludedAuthorities, null);
- return state;
- }
-
- @Override
- public State[] newArray(int size) {
- return new State[size];
- }
- };
- }
-
void setDisplayAdvancedDevices(boolean display) {
State state = getDisplayState();
LocalPreferences.setDisplayAdvancedDevices(this, display);
@@ -534,7 +432,7 @@
return getDisplayState().stack.peek();
}
- public Executor getCurrentExecutor() {
+ public Executor getExecutorForCurrentDirectory() {
final DocumentInfo cwd = getCurrentDirectory();
if (cwd != null && cwd.authority != null) {
return ProviderExecutor.forAuthority(cwd.authority);
@@ -664,6 +562,33 @@
}
}
+ final class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
+ private Uri mRootUri;
+
+ public RestoreRootTask(Uri rootUri) {
+ mRootUri = rootUri;
+ }
+
+ @Override
+ protected RootInfo doInBackground(Void... params) {
+ final String rootId = DocumentsContract.getRootId(mRootUri);
+ return mRoots.getRootOneshot(mRootUri.getAuthority(), rootId);
+ }
+
+ @Override
+ protected void onPostExecute(RootInfo root) {
+ if (isDestroyed()) return;
+ mState.restored = true;
+
+ if (root != null) {
+ onRootPicked(root);
+ } else {
+ Log.w(mTag, "Failed to find root: " + mRootUri);
+ finish();
+ }
+ }
+ }
+
final class ItemSelectedListener implements OnItemSelectedListener {
boolean mIgnoreNextNavigation;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index f8ec8f1..f1492dc7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -16,6 +16,7 @@
package com.android.documentsui;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -56,7 +57,6 @@
public class CopyService extends IntentService {
public static final String TAG = "CopyService";
- public static final boolean DEBUG = false;
private static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST";
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 279b0e0..5eacf21 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -16,15 +16,15 @@
package com.android.documentsui;
-import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE;
-import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE_ALL;
-import static com.android.documentsui.BaseActivity.State.ACTION_CREATE;
-import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
-import static com.android.documentsui.BaseActivity.State.MODE_GRID;
-import static com.android.documentsui.BaseActivity.State.MODE_LIST;
-import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.Shared.TAG;
+import static com.android.documentsui.State.ACTION_BROWSE;
+import static com.android.documentsui.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_MANAGE;
+import static com.android.documentsui.State.MODE_GRID;
+import static com.android.documentsui.State.MODE_LIST;
+import static com.android.documentsui.State.MODE_UNKNOWN;
+import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -77,6 +77,7 @@
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.TypedValue;
import android.view.ActionMode;
import android.view.DragEvent;
import android.view.GestureDetector;
@@ -93,14 +94,13 @@
import android.widget.Toast;
import com.android.documentsui.BaseActivity.DocumentContext;
-import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.MultiSelectManager.Selection;
import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;
-import com.android.internal.util.Preconditions;
+
import com.google.common.collect.Lists;
import java.util.ArrayList;
@@ -124,7 +124,6 @@
public static final int REQUEST_COPY_DESTINATION = 1;
private static final int LOADER_ID = 42;
- private static final boolean DEBUG = false;
private static final boolean DEBUG_ENABLE_DND = false;
private static final String EXTRA_TYPE = "type";
@@ -134,6 +133,7 @@
private static final String EXTRA_IGNORE_STATE = "ignoreState";
private Model mModel;
+ private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -158,6 +158,9 @@
private GridLayoutManager mGridLayout;
private int mColumnCount = 1; // This will get updated when layout changes.
+ private MessageBar mMessageBar;
+ private View mProgressBar;
+
public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
show(fm, TYPE_NORMAL, root, doc, null, anim);
}
@@ -219,6 +222,9 @@
final Resources res = context.getResources();
final View view = inflater.inflate(R.layout.fragment_directory, container, false);
+ mMessageBar = MessageBar.create(getChildFragmentManager());
+ mProgressBar = view.findViewById(R.id.progressbar);
+
mEmptyView = view.findViewById(android.R.id.empty);
mRecView = (RecyclerView) view.findViewById(R.id.recyclerView);
@@ -306,9 +312,8 @@
: MultiSelectManager.MODE_SINGLE);
selMgr.addCallback(new SelectionModeListener());
- mModel = new Model(context, selMgr);
- mModel.setSelectionManager(selMgr);
- mModel.addUpdateListener(mAdapter);
+ mModel = new Model(context, selMgr, mAdapter);
+ mModel.addUpdateListener(mModelUpdateListener);
mType = getArguments().getInt(EXTRA_TYPE);
mStateKey = buildStateKey(root, doc);
@@ -361,21 +366,6 @@
@Override
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
- if (result == null || result.exception != null) {
- // onBackPressed does a fragment transaction, which can't be done inside
- // onLoadFinished
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final Activity activity = getActivity();
- if (activity != null) {
- activity.onBackPressed();
- }
- }
- });
- return;
- }
-
if (!isAdded()) return;
mModel.update(result);
@@ -610,7 +600,7 @@
@Override
public boolean onBeforeItemStateChange(int position, boolean selected) {
- // Directories and footer items cannot be checked
+ // Directories cannot be checked
if (selected) {
final Cursor cursor = mModel.getItem(position);
checkNotNull(cursor, "Cursor cannot be null.");
@@ -623,7 +613,6 @@
@Override
public void onItemStateChanged(int position, boolean selected) {
-
final Cursor cursor = mModel.getItem(position);
checkNotNull(cursor, "Cursor cannot be null.");
@@ -636,23 +625,25 @@
@Override
public void onSelectionChanged() {
mModel.getSelection(mSelected);
+ TypedValue color = new TypedValue();
if (mSelected.size() > 0) {
if (DEBUG) Log.d(TAG, "Maybe starting action mode.");
if (mActionMode == null) {
if (DEBUG) Log.d(TAG, "Yeah. Starting action mode.");
mActionMode = getActivity().startActionMode(this);
- getActivity().getWindow().setStatusBarColor(
- getResources().getColor(R.color.action_mode_status_bar_background));
}
+ getActivity().getTheme().resolveAttribute(
+ R.attr.colorActionMode, color, true);
updateActionMenu();
} else {
if (DEBUG) Log.d(TAG, "Finishing action mode.");
if (mActionMode != null) {
mActionMode.finish();
}
- getActivity().getWindow().setStatusBarColor(
- getResources().getColor(R.color.status_bar_background));
+ getActivity().getTheme().resolveAttribute(
+ android.R.attr.colorPrimaryDark, color, true);
}
+ getActivity().getWindow().setStatusBarColor(color.data);
if (mActionMode != null) {
mActionMode.setTitle(TextUtils.formatSelectedCount(mSelected.size()));
@@ -707,8 +698,9 @@
return true;
} else if (id == R.id.menu_delete) {
- deleteDocuments(selection);
+ // Exit selection mode first, so we avoid deselecting deleted documents.
mode.finish();
+ deleteDocuments(selection);
return true;
} else if (id == R.id.menu_copy_to) {
@@ -717,8 +709,9 @@
return true;
} else if (id == R.id.menu_move_to) {
- transferDocuments(selection, CopyService.TRANSFER_MODE_MOVE);
+ // Exit selection mode first, so we avoid deselecting deleted documents.
mode.finish();
+ transferDocuments(selection, CopyService.TRANSFER_MODE_MOVE);
return true;
} else if (id == R.id.menu_copy_to_clipboard) {
@@ -827,8 +820,18 @@
if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) {
mModel.undoDeletion();
} else {
- // TODO: Use a listener rather than pushing the view.
- mModel.finalizeDeletion(DirectoryFragment.this.getView());
+ mModel.finalizeDeletion(
+ new Runnable() {
+ @Override
+ public void run() {
+ Snackbar.make(
+ DirectoryFragment.this.getView(),
+ R.string.toast_failed_delete,
+ Snackbar.LENGTH_LONG)
+ .show();
+
+ }
+ });
}
}
})
@@ -867,79 +870,6 @@
return ((BaseActivity) fragment.getActivity()).getDisplayState();
}
- private static abstract class Footer {
- private final int mItemViewType;
-
- public Footer(int itemViewType) {
- mItemViewType = itemViewType;
- }
-
- public abstract View getView(View convertView, ViewGroup parent);
-
- public int getItemViewType() {
- return mItemViewType;
- }
- }
-
- private class LoadingFooter extends Footer {
- public LoadingFooter() {
- super(1);
- }
-
- @Override
- public View getView(View convertView, ViewGroup parent) {
- final Context context = parent.getContext();
- final State state = getDisplayState(DirectoryFragment.this);
-
- if (convertView == null) {
- final LayoutInflater inflater = LayoutInflater.from(context);
- if (state.derivedMode == MODE_LIST) {
- convertView = inflater.inflate(R.layout.item_loading_list, parent, false);
- } else if (state.derivedMode == MODE_GRID) {
- convertView = inflater.inflate(R.layout.item_loading_grid, parent, false);
- } else {
- throw new IllegalStateException();
- }
- }
-
- return convertView;
- }
- }
-
- private class MessageFooter extends Footer {
- private final int mIcon;
- private final String mMessage;
-
- public MessageFooter(int itemViewType, int icon, String message) {
- super(itemViewType);
- mIcon = icon;
- mMessage = message;
- }
-
- @Override
- public View getView(View convertView, ViewGroup parent) {
- final Context context = parent.getContext();
- final State state = getDisplayState(DirectoryFragment.this);
-
- if (convertView == null) {
- final LayoutInflater inflater = LayoutInflater.from(context);
- if (state.derivedMode == MODE_LIST) {
- convertView = inflater.inflate(R.layout.item_message_list, parent, false);
- } else if (state.derivedMode == MODE_GRID) {
- convertView = inflater.inflate(R.layout.item_message_grid, parent, false);
- } else {
- throw new IllegalStateException();
- }
- }
-
- final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- icon.setImageResource(mIcon);
- title.setText(mMessage);
- return convertView;
- }
- }
-
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
@@ -953,46 +883,39 @@
}
}
- private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder>
- implements Model.UpdateListener {
+ void showEmptyView() {
+ mEmptyView.setVisibility(View.VISIBLE);
+ mRecView.setVisibility(View.GONE);
+ TextView msg = (TextView) mEmptyView.findViewById(R.id.message);
+ msg.setText(R.string.empty);
+ // No retry button for the empty view.
+ mEmptyView.findViewById(R.id.button_retry).setVisibility(View.GONE);
+ }
+
+ void showErrorView() {
+ mEmptyView.setVisibility(View.VISIBLE);
+ mRecView.setVisibility(View.GONE);
+ TextView msg = (TextView) mEmptyView.findViewById(R.id.message);
+ msg.setText(R.string.query_error);
+ // TODO: Enable this once the retry button does something.
+ mEmptyView.findViewById(R.id.button_retry).setVisibility(View.GONE);
+ }
+
+ void showRecyclerView() {
+ mEmptyView.setVisibility(View.GONE);
+ mRecView.setVisibility(View.VISIBLE);
+ }
+
+ private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> {
private final Context mContext;
private final LayoutInflater mInflater;
- // TODO: Bring back support for footers.
- private final List<Footer> mFooters = new ArrayList<>();
public DocumentsAdapter(Context context) {
mContext = context;
mInflater = LayoutInflater.from(context);
}
- public void onModelUpdate(Model model) {
- mFooters.clear();
- if (model.info != null) {
- mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_info, model.info));
- }
- if (model.error != null) {
- mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, model.error));
- }
- if (model.isLoading()) {
- mFooters.add(new LoadingFooter());
- }
-
- if (model.isEmpty()) {
- mEmptyView.setVisibility(View.VISIBLE);
- } else {
- mEmptyView.setVisibility(View.GONE);
- }
-
- notifyDataSetChanged();
- }
-
- public void onModelUpdateFailed(Exception e) {
- String error = getString(R.string.query_error);
- mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, error));
- notifyDataSetChanged();
- }
-
@Override
public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final State state = getDisplayState(DirectoryFragment.this);
@@ -1209,19 +1132,8 @@
@Override
public int getItemCount() {
return mModel.getItemCount();
- // return mCursorCount + mFooters.size();
}
- @Override
- public int getItemViewType(int position) {
- final int itemCount = mModel.getItemCount();
- if (position < itemCount) {
- return 0;
- } else {
- position -= itemCount;
- return mFooters.get(position).getItemViewType();
- }
- }
}
private static String formatTime(Context context, long when) {
@@ -1506,7 +1418,8 @@
getClipDataFromDocuments(docs),
new DrawableShadowBuilder(getDragShadowIcon(docs)),
null,
- View.DRAG_FLAG_GLOBAL
+ View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
+ View.DRAG_FLAG_GLOBAL_URI_WRITE
);
return true;
}
@@ -1647,9 +1560,9 @@
}
private FragmentTuner pickFragmentTuner(final State state) {
- return state.action == ACTION_BROWSE_ALL
+ return state.action == ACTION_BROWSE
? new FilesTuner()
- : new DefaultTuner(state);
+ : new DefaultTuner(state.action);
}
/**
@@ -1686,15 +1599,14 @@
*/
private static final class DefaultTuner implements FragmentTuner {
- private final State mState;
+ private final boolean mManaging;
- public DefaultTuner(State state) {
- mState = state;
+ public DefaultTuner(int action) {
+ mManaging = (action == ACTION_MANAGE);
}
@Override
public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
- Preconditions.checkState(mState.action != ACTION_BROWSE_ALL);
final MenuItem open = menu.findItem(R.id.menu_open);
final MenuItem share = menu.findItem(R.id.menu_share);
@@ -1703,14 +1615,11 @@
final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
final MenuItem copyToClipboard = menu.findItem(R.id.menu_copy_to_clipboard);
- final boolean manageOrBrowse = (mState.action == ACTION_MANAGE
- || mState.action == ACTION_BROWSE);
-
- open.setVisible(!manageOrBrowse);
- share.setVisible(manageOrBrowse);
- delete.setVisible(manageOrBrowse && canDelete);
+ open.setVisible(!mManaging);
+ share.setVisible(mManaging);
+ delete.setVisible(mManaging && canDelete);
// Disable copying from the Recents view.
- copyTo.setVisible(manageOrBrowse && dirType != TYPE_RECENT_OPEN);
+ copyTo.setVisible(mManaging && dirType != TYPE_RECENT_OPEN);
moveTo.setVisible(SystemProperties.getBoolean("debug.documentsui.enable_move", false));
// Only shown in files mode.
@@ -1727,6 +1636,7 @@
private static final class FilesTuner implements FragmentTuner {
@Override
public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+
menu.findItem(R.id.menu_share).setVisible(true);
menu.findItem(R.id.menu_delete).setVisible(canDelete);
menu.findItem(R.id.menu_copy_to_clipboard).setVisible(true);
@@ -1746,6 +1656,7 @@
@VisibleForTesting
public static final class Model implements DocumentContext {
private MultiSelectManager mSelectionManager;
+ private RecyclerView.Adapter<?> mViewAdapter;
private Context mContext;
private int mCursorCount;
private boolean mIsLoading;
@@ -1755,17 +1666,11 @@
@Nullable private String info;
@Nullable private String error;
- Model(Context context, MultiSelectManager selectionManager) {
+ Model(Context context, MultiSelectManager selectionManager,
+ RecyclerView.Adapter<?> viewAdapter) {
mContext = context;
mSelectionManager = selectionManager;
- }
-
- /**
- * Sets the selection manager used by the model.
- * TODO: the model should instantiate the selection manager. See onActivityCreated.
- */
- void setSelectionManager(MultiSelectManager mgr) {
- mSelectionManager = mgr;
+ mViewAdapter = viewAdapter;
}
/**
@@ -1809,13 +1714,13 @@
info = null;
error = null;
mIsLoading = false;
- if (mUpdateListener != null) mUpdateListener.onModelUpdate(this);
+ mUpdateListener.onModelUpdate(this);
return;
}
if (result.exception != null) {
Log.e(TAG, "Error while loading directory contents", result.exception);
- if (mUpdateListener != null) mUpdateListener.onModelUpdateFailed(result.exception);
+ mUpdateListener.onModelUpdateFailed(result.exception);
return;
}
@@ -1829,7 +1734,7 @@
mIsLoading = extras.getBoolean(DocumentsContract.EXTRA_LOADING, false);
}
- if (mUpdateListener != null) mUpdateListener.onModelUpdate(this);
+ mUpdateListener.onModelUpdate(this);
}
int getItemCount() {
@@ -1930,7 +1835,7 @@
int position = selected.get(i);
if (DEBUG) Log.d(TAG, "Marked position " + position + " for deletion");
mMarkedForDeletion.append(position, true);
- if (mUpdateListener != null) mUpdateListener.notifyItemRemoved(position);
+ mViewAdapter.notifyItemRemoved(position);
}
}
@@ -1945,7 +1850,7 @@
for (int i = 0; i < size; ++i) {
final int position = mMarkedForDeletion.keyAt(i);
mMarkedForDeletion.put(position, false);
- if (mUpdateListener != null) mUpdateListener.notifyItemInserted(position);
+ mViewAdapter.notifyItemInserted(position);
}
// Then, clear the deletion list.
@@ -1959,21 +1864,9 @@
* @param view The view which will be used to interact with the user (e.g. surfacing
* snackbars) for errors, info, etc.
*/
- void finalizeDeletion(final View view) {
+ void finalizeDeletion(Runnable errorCallback) {
final ContentResolver resolver = mContext.getContentResolver();
- DeleteFilesTask task = new DeleteFilesTask(
- resolver,
- new Runnable() {
- @Override
- public void run() {
- Snackbar.make(
- view,
- R.string.toast_failed_delete,
- Snackbar.LENGTH_LONG)
- .show();
-
- }
- });
+ DeleteFilesTask task = new DeleteFilesTask(resolver, errorCallback);
task.execute();
}
@@ -2040,26 +1933,41 @@
mUpdateListener = listener;
}
- interface UpdateListener {
+ static class UpdateListener {
/**
* Called when a successful update has occurred.
*/
- void onModelUpdate(Model model);
+ void onModelUpdate(Model model) {}
/**
* Called when an update has been attempted but failed.
*/
- void onModelUpdateFailed(Exception e);
+ void onModelUpdateFailed(Exception e) {}
+ }
+ }
- /**
- * Called when an item has been removed from the model.
- */
- void notifyItemRemoved(int position);
+ private class ModelUpdateListener extends Model.UpdateListener {
+ @Override
+ public void onModelUpdate(Model model) {
+ if (model.info != null || model.error != null) {
+ mMessageBar.setInfo(model.info);
+ mMessageBar.setError(model.error);
+ mMessageBar.show();
+ }
- /**
- * Called when an item has been added to the model.
- */
- void notifyItemInserted(int position);
+ mProgressBar.setVisibility(model.isLoading() ? View.VISIBLE : View.GONE);
+
+ if (model.isEmpty()) {
+ showEmptyView();
+ } else {
+ showRecyclerView();
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onModelUpdateFailed(Exception e) {
+ showErrorView();
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 0edb241..bb82b38 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -16,12 +16,12 @@
package com.android.documentsui;
-import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN;
import static com.android.documentsui.Shared.TAG;
+import static com.android.documentsui.State.MODE_UNKNOWN;
+import static com.android.documentsui.State.SORT_ORDER_DISPLAY_NAME;
+import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.State.SORT_ORDER_SIZE;
+import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import android.content.AsyncTaskLoader;
@@ -37,7 +37,6 @@
import android.provider.DocumentsContract.Document;
import android.util.Log;
-import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
index 4893652..000b92a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
@@ -18,9 +18,9 @@
import android.content.Context;
import android.util.AttributeSet;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
-public class DirectoryView extends FrameLayout {
+public class DirectoryView extends LinearLayout {
private float mPosition = 0f;
private int mWidth;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index fdc4bb0..4658fe3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -16,20 +16,16 @@
package com.android.documentsui;
-import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE;
-import static com.android.documentsui.BaseActivity.State.ACTION_CREATE;
-import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
-import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_COPY_DESTINATION;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
-import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_GET_CONTENT;
+import static com.android.documentsui.State.ACTION_OPEN;
+import static com.android.documentsui.State.ACTION_OPEN_COPY_DESTINATION;
+import static com.android.documentsui.State.ACTION_OPEN_TREE;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
-import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentProviderClient;
@@ -45,7 +41,6 @@
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Root;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -67,7 +62,7 @@
public class DocumentsActivity extends BaseActivity {
private static final int CODE_FORWARD = 42;
- public static final String TAG = "Documents";
+ private static final String TAG = "DocumentsActivity";
private boolean mShowAsDialog;
@@ -76,8 +71,6 @@
private Toolbar mRootsToolbar;
- private DirectoryContainerView mDirectoryContainer;
-
private ItemSelectedListener mStackListener;
private BaseAdapter mStackAdapter;
@@ -90,8 +83,7 @@
super.onCreate(icicle);
final Resources res = getResources();
- mShowAsDialog = res.getBoolean(R.bool.show_as_dialog) && mState.action != ACTION_MANAGE &&
- mState.action != ACTION_BROWSE;
+ mShowAsDialog = res.getBoolean(R.bool.show_as_dialog);
if (!mShowAsDialog) {
setTheme(R.style.DocumentsNonDialogTheme);
@@ -116,11 +108,7 @@
mDrawer = DrawerController.create(this);
}
- mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
-
mToolbar = (Toolbar) findViewById(R.id.toolbar);
- mToolbar.setTitleTextAppearance(context,
- android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
mStackAdapter = new StackAdapter();
mStackListener = new ItemSelectedListener();
@@ -128,21 +116,9 @@
mToolbarStack.setOnItemSelectedListener(mStackListener);
mRootsToolbar = (Toolbar) findViewById(R.id.roots_toolbar);
- if (mRootsToolbar != null) {
- mRootsToolbar.setTitleTextAppearance(context,
- android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
- }
setActionBar(mToolbar);
- // Hide roots when we're managing a specific root
- if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
- mDrawer.lockClosed();
- if (mShowAsDialog) {
- findViewById(R.id.container_roots).setVisibility(View.GONE);
- }
- }
-
if (mState.action == ACTION_CREATE) {
final String mimeType = getIntent().getType();
final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
@@ -168,20 +144,15 @@
// In this case, we set the activity title in AsyncTask.onPostExecute(). To prevent
// talkback from reading aloud the default title, we clear it here.
setTitle("");
- if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
- final Uri rootUri = getIntent().getData();
- new RestoreRootTask(rootUri).executeOnExecutor(getCurrentExecutor());
- } else {
- new RestoreStackTask().execute();
- }
+ new RestoreStackTask().execute();
} else {
onCurrentDirectoryChanged(ANIM_NONE);
}
}
@Override
- State buildDefaultState() {
- State state = new State();
+ State buildState() {
+ State state = buildDefaultState();
final Intent intent = getIntent();
final String action = intent.getAction();
@@ -193,10 +164,6 @@
state.action = ACTION_GET_CONTENT;
} else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
state.action = ACTION_OPEN_TREE;
- } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
- state.action = ACTION_MANAGE;
- } else if (DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT.equals(action)) {
- state.action = ACTION_BROWSE;
} else if (DocumentsIntent.ACTION_OPEN_COPY_DESTINATION.equals(action)) {
state.action = ACTION_OPEN_COPY_DESTINATION;
}
@@ -206,25 +173,6 @@
Intent.EXTRA_ALLOW_MULTIPLE, false);
}
- if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
- state.acceptMimes = new String[] { "*/*" };
- state.allowMultiple = true;
- } else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
- state.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
- } else {
- state.acceptMimes = new String[] { intent.getType() };
- }
-
- state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
- state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
- state.showAdvanced = state.forceAdvanced
- | LocalPreferences.getDisplayAdvancedDevices(this);
-
- if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
- state.showSize = true;
- } else {
- state.showSize = LocalPreferences.getDisplayFileSize(this);
- }
if (state.action == ACTION_OPEN_COPY_DESTINATION) {
state.directoryCopy = intent.getBooleanExtra(
BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
@@ -232,38 +180,9 @@
CopyService.TRANSFER_MODE_NONE);
}
- state.excludedAuthorities = getExcludedAuthorities();
-
return state;
}
- private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
- private Uri mRootUri;
-
- public RestoreRootTask(Uri rootUri) {
- mRootUri = rootUri;
- }
-
- @Override
- protected RootInfo doInBackground(Void... params) {
- final String rootId = DocumentsContract.getRootId(mRootUri);
- return mRoots.getRootOneshot(mRootUri.getAuthority(), rootId);
- }
-
- @Override
- protected void onPostExecute(RootInfo root) {
- if (isDestroyed()) return;
- mState.restored = true;
-
- if (root != null) {
- onRootPicked(root);
- } else {
- Log.w(TAG, "Failed to find root: " + mRootUri);
- finish();
- }
- }
- }
-
@Override
void onStackRestored(boolean restored, boolean external) {
// Show drawer when no stack restored, but only when requesting
@@ -405,8 +324,7 @@
final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
final MenuItem settings = menu.findItem(R.id.menu_settings);
- boolean fileSizeVisible = !(mState.action == ACTION_MANAGE
- || mState.action == ACTION_BROWSE);
+ boolean fileSizeVisible = mState.showSize && !mState.forceSize;
if (mState.action == ACTION_CREATE
|| mState.action == ACTION_OPEN_TREE
|| mState.action == ACTION_OPEN_COPY_DESTINATION) {
@@ -428,12 +346,9 @@
createDir.setVisible(false);
}
- advanced.setVisible(!(mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) &&
- !mState.forceAdvanced);
+ advanced.setVisible(!mState.forceAdvanced);
fileSize.setVisible(fileSizeVisible);
-
- settings.setVisible((mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE)
- && (root.flags & Root.FLAG_HAS_SETTINGS) != 0);
+ settings.setVisible(false);
return true;
}
@@ -444,18 +359,11 @@
}
@Override
- public State getDisplayState() {
- return mState;
- }
-
- @Override
void onDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
- mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
-
if (cwd == null) {
// No directory means recents
if (mState.action == ACTION_CREATE ||
@@ -499,11 +407,11 @@
}
void onSaveRequested(DocumentInfo replaceTarget) {
- new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
+ new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory());
}
void onSaveRequested(String mimeType, String displayName) {
- new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
+ new CreateFinishTask(mimeType, displayName).executeOnExecutor(getExecutorForCurrentDirectory());
}
@Override
@@ -519,41 +427,10 @@
openDirectory(doc);
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
// Explicit file picked, return
- new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getCurrentExecutor());
+ new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory());
} else if (mState.action == ACTION_CREATE) {
// Replace selected file
SaveFragment.get(fm).setReplaceTarget(doc);
- } else if (mState.action == ACTION_MANAGE) {
- // First try managing the document; we expect manager to filter
- // based on authority, so we don't grant.
- final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
- manage.setData(doc.derivedUri);
-
- try {
- startActivity(manage);
- } catch (ActivityNotFoundException ex) {
- // Fall back to viewing
- final Intent view = new Intent(Intent.ACTION_VIEW);
- view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- view.setData(doc.derivedUri);
-
- try {
- startActivity(view);
- } catch (ActivityNotFoundException ex2) {
- Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
- }
- }
- } else if (mState.action == ACTION_BROWSE) {
- // Go straight to viewing
- final Intent view = new Intent(Intent.ACTION_VIEW);
- view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- view.setData(doc.derivedUri);
-
- try {
- startActivity(view);
- } catch (ActivityNotFoundException ex) {
- Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
- }
}
}
@@ -565,7 +442,7 @@
for (int i = 0; i < size; i++) {
uris[i] = docs.get(i).derivedUri;
}
- new ExistingFinishTask(uris).executeOnExecutor(getCurrentExecutor());
+ new ExistingFinishTask(uris).executeOnExecutor(getExecutorForCurrentDirectory());
}
}
@@ -580,7 +457,7 @@
// Should not be reached.
throw new IllegalStateException("Invalid mState.action.");
}
- new PickFinishTask(result).executeOnExecutor(getCurrentExecutor());
+ new PickFinishTask(result).executeOnExecutor(getExecutorForCurrentDirectory());
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 7c445bf..e8d1088 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -16,8 +16,10 @@
package com.android.documentsui;
-import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkState;
import android.app.Activity;
import android.app.FragmentManager;
@@ -25,12 +27,9 @@
import android.content.ClipData;
import android.content.ContentResolver;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Root;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.KeyEvent;
@@ -47,7 +46,6 @@
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;
-import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
@@ -58,13 +56,10 @@
*/
public class FilesActivity extends BaseActivity {
- public static final String TAG = "StandaloneFileManagement";
- static final boolean DEBUG = false;
+ public static final String TAG = "FilesActivity";
private Toolbar mToolbar;
private Spinner mToolbarStack;
- private Toolbar mRootsToolbar;
- private DirectoryContainerView mDirectoryContainer;
private ItemSelectedListener mStackListener;
private BaseAdapter mStackAdapter;
private DocumentClipper mClipper;
@@ -77,40 +72,33 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- final Context context = this;
-
- mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
-
mToolbar = (Toolbar) findViewById(R.id.toolbar);
- mToolbar.setTitleTextAppearance(context,
- android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
mStackAdapter = new StackAdapter();
mStackListener = new ItemSelectedListener();
mToolbarStack = (Spinner) findViewById(R.id.stack);
mToolbarStack.setOnItemSelectedListener(mStackListener);
- mRootsToolbar = (Toolbar) findViewById(R.id.roots_toolbar);
- if (mRootsToolbar != null) {
- mRootsToolbar.setTitleTextAppearance(context,
- android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
- }
-
setActionBar(mToolbar);
mClipper = new DocumentClipper(this);
mDrawer = DrawerController.create(this);
- if (mDrawer.isPresent()) {
- setTheme(R.style.DocumentsNonDialogTheme);
- }
-
RootsFragment.show(getFragmentManager(), null);
if (!mState.restored) {
- new RestoreStackTask().execute();
+ Intent intent = getIntent();
+ Uri rootUri = intent.getData();
+
+ // If we've got a specific root to display, restore that root using a dedicated
+ // authority. That way a misbehaving provider won't result in an ANR.
+ if (rootUri != null && !LauncherActivity.isLaunchUri(rootUri)) {
+ new RestoreRootTask(rootUri).executeOnExecutor(
+ ProviderExecutor.forAuthority(rootUri.getAuthority()));
+ } else {
+ new RestoreStackTask().execute();
+ }
// Show a failure dialog if there was a failed operation.
- final Intent intent = getIntent();
final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
@@ -127,27 +115,21 @@
}
@Override
- State buildDefaultState() {
- State state = new State();
+ State buildState() {
+ State state = buildDefaultState();
final Intent intent = getIntent();
- state.action = State.ACTION_BROWSE_ALL;
- state.acceptMimes = new String[] { "*/*" };
+
+ state.action = State.ACTION_BROWSE;
state.allowMultiple = true;
- state.acceptMimes = new String[] { intent.getType() };
- // These options are specific to the DocumentsActivity.
- Preconditions.checkArgument(
- !intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
- Preconditions.checkArgument(
- !intent.hasExtra(DocumentsContract.EXTRA_SHOW_ADVANCED));
-
- state.showAdvanced = LocalPreferences.getDisplayAdvancedDevices(this);
- state.showSize = LocalPreferences.getDisplayFileSize(this);
+ // Options specific to the DocumentsActivity.
+ checkArgument(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
- if (stack != null)
+ if (stack != null) {
state.stack = stack;
+ }
return state;
}
@@ -159,6 +141,21 @@
}
@Override
+ public void onResume() {
+ super.onResume();
+
+ final RootInfo root = getCurrentRoot();
+
+ // If we're browsing a specific root, and that root went away, then we
+ // have no reason to hang around.
+ // TODO: Rather than just disappearing, maybe we should inform
+ // the user what has happened, let them close us. Less surprising.
+ if (mRoots.getRootBlocking(root.authority, root.rootId) == null) {
+ finish();
+ }
+ }
+
+ @Override
public void updateActionBar() {
final RootInfo root = getCurrentRoot();
@@ -214,19 +211,22 @@
menu.findItem(R.id.menu_file_size).setVisible(true);
menu.findItem(R.id.menu_advanced).setVisible(true);
- final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
- final MenuItem settings = menu.findItem(R.id.menu_settings);
+ final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
+ final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
boolean canCreateDir = canCreateDirectory();
createDir.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
createDir.setVisible(canCreateDir);
+ createDir.setEnabled(canCreateDir);
- settings.setVisible((getCurrentRoot().flags & Root.FLAG_HAS_SETTINGS) != 0);
+ newWindow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ newWindow.setVisible(mProductivityDevice);
+ newWindow.setEnabled(mProductivityDevice);
- pasteFromCb.setVisible(true);
pasteFromCb.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ pasteFromCb.setVisible(true);
pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
return shown;
@@ -234,30 +234,30 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- final int id = item.getItemId();
- if (id == R.id.menu_paste_from_clipboard) {
- DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
- dir = DirectoryFragment.get(getFragmentManager());
- dir.pasteFromClipboard();
- return true;
+ switch (item.getItemId()) {
+ case R.id.menu_create_dir:
+ checkState(canCreateDirectory());
+ showCreateDirectoryDialog();
+ return true;
+ case R.id.menu_new_window:
+ startActivity(LauncherActivity.createLaunchIntent(this));
+ return true;
+ case R.id.menu_paste_from_clipboard:
+ DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
+ dir = DirectoryFragment.get(getFragmentManager());
+ dir.pasteFromClipboard();
+ return true;
}
return super.onOptionsItemSelected(item);
}
@Override
- public State getDisplayState() {
- return mState;
- }
-
- @Override
void onDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
- mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
-
if (cwd == null) {
DirectoryFragment.showRecentsOpen(fm, anim);
@@ -334,19 +334,13 @@
dir = DirectoryFragment.get(getFragmentManager());
dir.selectAllFiles();
return true;
- case KeyEvent.KEYCODE_N:
- if (event.isShiftPressed() && canCreateDirectory()) {
- showCreateDirectoryDialog();
- return true;
- }
case KeyEvent.KEYCODE_C:
// TODO: Should be statically bound using alphabeticShortcut. See b/21330356.
dir = DirectoryFragment.get(getFragmentManager());
dir.copySelectedToClipboard();
- // TODO: Cancel action mode in directory fragment.
}
- return super.onKeyUp(keyCode, event);
+ return super.onKeyShortcut(keyCode, event);
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
index 9959265..a1213d2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
@@ -222,7 +222,7 @@
return context.getDrawable(R.drawable.ic_doc_album);
}
- if (mode == DocumentsActivity.State.MODE_GRID) {
+ if (mode == State.MODE_GRID) {
return context.getDrawable(R.drawable.ic_grid_folder);
} else {
return context.getDrawable(R.drawable.ic_doc_folder);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
new file mode 100644
index 0000000..c29937d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.AppTask;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Provides FilesActivity task grouping support. This allows multiple FilesActivities to be
+ * launched (a behavior imparted by way of {@code documentLaunchMode="intoExisting"} and
+ * our use of pseudo document {@link Uri}s. This also lets us move an existing task
+ * to the foreground when a suitable task exists.
+ *
+ * Requires that {@code documentLaunchMode="intoExisting"} be set on target activity.
+ *
+ */
+public class LauncherActivity extends Activity {
+
+ public static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ List<AppTask> tasks = activities.getAppTasks();
+
+ AppTask raiseTask = null;
+ for (AppTask task : tasks) {
+ Uri taskUri = task.getTaskInfo().baseIntent.getData();
+ if (taskUri != null && isLaunchUri(taskUri)) {
+ raiseTask = task;
+ }
+ }
+
+ if (raiseTask == null) {
+ launchFilesTask();
+ } else {
+ raiseFilesTask(activities, raiseTask.getTaskInfo());
+ }
+
+ finish();
+ }
+
+ private void launchFilesTask() {
+ Intent intent = createLaunchIntent(this);
+ startActivity(intent);
+ }
+
+ private void raiseFilesTask(ActivityManager activities, RecentTaskInfo task) {
+ activities.moveTaskToFront(task.id, 0);
+ }
+
+ static Intent createLaunchIntent(Context context) {
+ Intent intent = new Intent(context, FilesActivity.class);
+ intent.setData(buildLaunchUri());
+ return intent;
+ }
+
+ private static Uri buildLaunchUri() {
+ return new Uri.Builder()
+ .authority(LAUNCH_CONTROL_AUTHORITY)
+ .fragment(String.valueOf(System.currentTimeMillis()))
+ .build();
+ }
+
+ static boolean isLaunchUri(@Nullable Uri uri) {
+ return uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority());
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
new file mode 100644
index 0000000..4754899
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.State.ACTION_MANAGE;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.util.Log;
+import android.view.Menu;
+import android.view.View;
+import android.widget.BaseAdapter;
+import android.widget.Spinner;
+import android.widget.Toast;
+import android.widget.Toolbar;
+
+import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DurableUtils;
+import com.android.documentsui.model.RootInfo;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ManageRootActivity extends BaseActivity {
+ private static final int CODE_FORWARD = 42;
+ private static final String TAG = "ManageRootsActivity";
+
+ private Toolbar mToolbar;
+ private Spinner mToolbarStack;
+
+ private ItemSelectedListener mStackListener;
+ private BaseAdapter mStackAdapter;
+
+ public ManageRootActivity() {
+ super(R.layout.manage_roots_activity, TAG);
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Context context = this;
+
+ mDrawer = DrawerController.createDummy();
+
+ mToolbar = (Toolbar) findViewById(R.id.toolbar);
+ mToolbar.setTitleTextAppearance(context,
+ android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
+
+ mStackAdapter = new StackAdapter();
+ mStackListener = new ItemSelectedListener();
+ mToolbarStack = (Spinner) findViewById(R.id.stack);
+ mToolbarStack.setOnItemSelectedListener(mStackListener);
+
+ setActionBar(mToolbar);
+
+ if (!mState.restored) {
+ // In this case, we set the activity title in AsyncTask.onPostExecute(). To prevent
+ // talkback from reading aloud the default title, we clear it here.
+ setTitle("");
+ final Uri rootUri = getIntent().getData();
+ new RestoreRootTask(rootUri).executeOnExecutor(getExecutorForCurrentDirectory());
+ } else {
+ onCurrentDirectoryChanged(ANIM_NONE);
+ }
+ }
+
+ @Override
+ State buildState() {
+ State state = buildDefaultState();
+
+ state.action = ACTION_MANAGE;
+ state.acceptMimes = new String[] { "*/*" };
+ state.allowMultiple = true;
+ state.showSize = true;
+ state.excludedAuthorities = getExcludedAuthorities();
+
+ return state;
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ updateActionBar();
+ }
+
+ @Override
+ public void updateActionBar() {
+ // No navigation in manage root mode.
+ mToolbar.setNavigationIcon(null);
+ mToolbar.setNavigationOnClickListener(null);
+
+ if (mSearchManager.isExpanded()) {
+ mToolbar.setTitle(null);
+ mToolbarStack.setVisibility(View.GONE);
+ mToolbarStack.setAdapter(null);
+ } else {
+ if (mState.stack.size() <= 1) {
+ mToolbar.setTitle(getCurrentRoot().title);
+ mToolbarStack.setVisibility(View.GONE);
+ mToolbarStack.setAdapter(null);
+ } else {
+ mToolbar.setTitle(null);
+ mToolbarStack.setVisibility(View.VISIBLE);
+ mToolbarStack.setAdapter(mStackAdapter);
+
+ mStackListener.mIgnoreNextNavigation = true;
+ mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
+ }
+ }
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ return true;
+ }
+
+ @Override
+ void onDirectoryChanged(int anim) {
+ final FragmentManager fm = getFragmentManager();
+ final RootInfo root = getCurrentRoot();
+ final DocumentInfo cwd = getCurrentDirectory();
+
+ // If started in manage roots mode, there has to be a cwd (i.e. the root dir of the managed
+ // root).
+ Preconditions.checkNotNull(cwd);
+
+ if (mState.currentSearch != null) {
+ // Ongoing search
+ DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
+ } else {
+ // Normal boring directory
+ DirectoryFragment.showNormal(fm, root, cwd, anim);
+ }
+ }
+
+ @Override
+ public void onDocumentPicked(DocumentInfo doc, DocumentContext context) {
+ final FragmentManager fm = getFragmentManager();
+ if (doc.isDirectory()) {
+ openDirectory(doc);
+ } else {
+ // First try managing the document; we expect manager to filter
+ // based on authority, so we don't grant.
+ final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
+ manage.setData(doc.derivedUri);
+
+ try {
+ startActivity(manage);
+ } catch (ActivityNotFoundException ex) {
+ // Fall back to viewing
+ final Intent view = new Intent(Intent.ACTION_VIEW);
+ view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ view.setData(doc.derivedUri);
+
+ try {
+ startActivity(view);
+ } catch (ActivityNotFoundException ex2) {
+ Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDocumentsPicked(List<DocumentInfo> docs) {}
+
+ @Override
+ void saveStackBlocking() {
+ final ContentResolver resolver = getContentResolver();
+ final ContentValues values = new ContentValues();
+
+ final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
+
+ // Remember location for next app launch
+ final String packageName = getCallingPackageMaybeExtra();
+ values.clear();
+ values.put(ResumeColumns.STACK, rawStack);
+ values.put(ResumeColumns.EXTERNAL, 0);
+ resolver.insert(RecentsProvider.buildResume(packageName), values);
+ }
+
+ @Override
+ void onTaskFinished(Uri... uris) {
+ Log.d(TAG, "onFinished() " + Arrays.toString(uris));
+
+ final Intent intent = new Intent();
+ if (uris.length == 1) {
+ intent.setData(uris[0]);
+ } else if (uris.length > 1) {
+ final ClipData clipData = new ClipData(
+ null, mState.acceptMimes, new ClipData.Item(uris[0]));
+ for (int i = 1; i < uris.length; i++) {
+ clipData.addItem(new ClipData.Item(uris[i]));
+ }
+ intent.setClipData(clipData);
+ }
+
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ }
+
+ public static ManageRootActivity get(Fragment fragment) {
+ return (ManageRootActivity) fragment.getActivity();
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java b/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java
new file mode 100644
index 0000000..312d53b
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * A message bar displaying some info/error messages and a Dismiss button.
+ */
+public class MessageBar extends Fragment {
+ private View mView;
+ private ViewGroup mContainer;
+
+ /**
+ * Creates an instance of a MessageBar. Note that the new MessagBar is not visible by default,
+ * and has to be shown by calling MessageBar.show.
+ */
+ public static MessageBar create(FragmentManager fm) {
+ final MessageBar fragment = new MessageBar();
+
+ final FragmentTransaction ft = fm.beginTransaction();
+ ft.replace(R.id.container_message_bar, fragment);
+ ft.commitAllowingStateLoss();
+
+ return fragment;
+ }
+
+ /**
+ * Sets the info message. Can be null, in which case no info message will be displayed. The
+ * message bar layout will be adjusted accordingly.
+ */
+ public void setInfo(@Nullable String info) {
+ View infoContainer = mView.findViewById(R.id.container_info);
+ if (info != null) {
+ TextView infoText = (TextView) mView.findViewById(R.id.textview_info);
+ infoText.setText(info);
+ infoContainer.setVisibility(View.VISIBLE);
+ } else {
+ infoContainer.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Sets the error message. Can be null, in which case no error message will be displayed. The
+ * message bar layout will be adjusted accordingly.
+ */
+ public void setError(@Nullable String error) {
+ View errorView = mView.findViewById(R.id.container_error);
+ if (error != null) {
+ TextView errorText = (TextView) mView.findViewById(R.id.textview_error);
+ errorText.setText(error);
+ errorView.setVisibility(View.VISIBLE);
+ } else {
+ errorView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ mView = inflater.inflate(R.layout.fragment_message_bar, container, false);
+
+ ImageView infoIcon = (ImageView) mView.findViewById(R.id.icon_info);
+ infoIcon.setImageResource(R.drawable.ic_dialog_info);
+
+ ImageView errorIcon = (ImageView) mView.findViewById(R.id.icon_error);
+ errorIcon.setImageResource(R.drawable.ic_dialog_alert);
+
+ Button dismiss = (Button) mView.findViewById(R.id.button_dismiss);
+ dismiss.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ hide();
+ }
+ });
+
+ mContainer = container;
+
+ return mView;
+ }
+
+ void hide() {
+ // The container view is used to show/hide the error bar. If a container is not provided,
+ // fall back to showing/hiding the error bar View, which also works, but does not provide
+ // the same animated transition.
+ if (mContainer != null) {
+ mContainer.setVisibility(View.GONE);
+ } else {
+ mView.setVisibility(View.GONE);
+ }
+ }
+
+ void show() {
+ // The container view is used to show/hide the error bar. If a container is not provided,
+ // fall back to showing/hiding the error bar View, which also works, but does not provide
+ // the same animated transition.
+ if (mContainer != null) {
+ mContainer.setVisibility(View.VISIBLE);
+ } else {
+ mView.setVisibility(View.VISIBLE);
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
index 5930056..858fb42 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
@@ -45,6 +45,8 @@
import com.android.documentsui.Events.InputEvent;
import com.android.documentsui.Events.MotionInputEvent;
+import com.google.android.collect.Lists;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -113,8 +115,11 @@
}
};
- CompositeOnGestureListener<? extends Object> compositeListener =
- new CompositeOnGestureListener<>(listener, gestureDelegate);
+ CompositeOnGestureListener compositeListener =
+ new CompositeOnGestureListener(
+ Lists.<OnGestureListener>newArrayList(listener, gestureDelegate),
+ Lists.<OnDoubleTapListener>newArrayList(listener, gestureDelegate));
+
final GestureDetector detector =
new GestureDetector(recyclerView.getContext(), compositeListener);
@@ -360,8 +365,8 @@
// To make this more correct, we'd need to update the Ranger class to return
// information about what has changed.
notifySelectionChanged();
- } else if (toggleSelection(input.getItemPosition())) {
- notifySelectionChanged();
+ } else {
+ toggleSelection(input.getItemPosition());
}
}
@@ -370,14 +375,13 @@
* a new Ranger (range selection manager) at that point is created.
*
* @param position
- * @return True if state changed.
*/
- private boolean toggleSelection(int position) {
+ private void toggleSelection(int position) {
// Position may be special "no position" during certain
// transitional phases. If so, skip handling of the event.
if (position == RecyclerView.NO_POSITION) {
if (DEBUG) Log.d(TAG, "Ignoring toggle for element with no position.");
- return false;
+ return;
}
boolean changed = false;
@@ -386,7 +390,7 @@
} else {
boolean canSelect = notifyBeforeItemStateChange(position, true);
if (!canSelect) {
- return false;
+ return;
}
if (mSingleSelect && !mSelection.isEmpty()) {
clearSelectionQuietly();
@@ -402,7 +406,9 @@
changed = true;
}
- return changed;
+ if (changed) {
+ notifySelectionChanged();
+ }
}
/**
@@ -1060,21 +1066,23 @@
* @template A gestureDelegate that implements both {@link OnGestureListener}
* and {@link OnDoubleTapListener}
*/
- private static final class
- CompositeOnGestureListener<L extends OnGestureListener & OnDoubleTapListener>
+ private static final class CompositeOnGestureListener
implements OnGestureListener, OnDoubleTapListener {
- private L[] mListeners;
+ private List<OnGestureListener> mGestureListeners;
+ private List<OnDoubleTapListener> mTapListeners;
- @SafeVarargs
- public CompositeOnGestureListener(L... listeners) {
- mListeners = listeners;
+ public CompositeOnGestureListener(
+ List<OnGestureListener> gestureListeners,
+ List<OnDoubleTapListener> tapListeners) {
+ mGestureListeners = gestureListeners;
+ mTapListeners = tapListeners;
}
@Override
public boolean onDown(MotionEvent e) {
- for (int i = 0; i < mListeners.length; i++) {
- if (mListeners[i].onDown(e)) {
+ for (OnGestureListener l : mGestureListeners) {
+ if (l.onDown(e)) {
return true;
}
}
@@ -1083,15 +1091,15 @@
@Override
public void onShowPress(MotionEvent e) {
- for (int i = 0; i < mListeners.length; i++) {
- mListeners[i].onShowPress(e);
+ for (OnGestureListener l : mGestureListeners) {
+ l.onShowPress(e);
}
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
- for (int i = 0; i < mListeners.length; i++) {
- if (mListeners[i].onSingleTapUp(e)) {
+ for (OnGestureListener l : mGestureListeners) {
+ if (l.onSingleTapUp(e)) {
return true;
}
}
@@ -1100,8 +1108,8 @@
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- for (int i = 0; i < mListeners.length; i++) {
- if (mListeners[i].onScroll(e1, e2, distanceX, distanceY)) {
+ for (OnGestureListener l : mGestureListeners) {
+ if (l.onScroll(e1, e2, distanceX, distanceY)) {
return true;
}
}
@@ -1110,15 +1118,15 @@
@Override
public void onLongPress(MotionEvent e) {
- for (int i = 0; i < mListeners.length; i++) {
- mListeners[i].onLongPress(e);
+ for (OnGestureListener l : mGestureListeners) {
+ l.onLongPress(e);
}
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- for (int i = 0; i < mListeners.length; i++) {
- if (mListeners[i].onFling(e1, e2, velocityX, velocityY)) {
+ for (OnGestureListener l : mGestureListeners) {
+ if (l.onFling(e1, e2, velocityX, velocityY)) {
return true;
}
}
@@ -1127,8 +1135,8 @@
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
- for (int i = 0; i < mListeners.length; i++) {
- if (mListeners[i].onSingleTapConfirmed(e)) {
+ for (OnDoubleTapListener listener : mTapListeners) {
+ if (listener.onSingleTapConfirmed(e)) {
return true;
}
}
@@ -1137,8 +1145,8 @@
@Override
public boolean onDoubleTap(MotionEvent e) {
- for (int i = 0; i < mListeners.length; i++) {
- if (mListeners[i].onDoubleTap(e)) {
+ for (OnDoubleTapListener listener : mTapListeners) {
+ if (listener.onDoubleTap(e)) {
return true;
}
}
@@ -1147,8 +1155,8 @@
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
- for (int i = 0; i < mListeners.length; i++) {
- if (mListeners[i].onDoubleTapEvent(e)) {
+ for (OnDoubleTapListener listener : mTapListeners) {
+ if (listener.onDoubleTapEvent(e)) {
return true;
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index 5f6a5e9..48e28dc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -21,7 +21,6 @@
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
-import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -29,8 +28,6 @@
import com.android.documentsui.model.DocumentInfo;
-import java.util.Locale;
-
/**
* Display pick confirmation bar, usually for selecting a directory.
*/
@@ -93,7 +90,7 @@
};
/**
- * @param action Which action defined in BaseActivity.State is the picker shown for.
+ * @param action Which action defined in State is the picker shown for.
*/
public void setPickTarget(int action, int transferMode, DocumentInfo pickTarget) {
mAction = action;
@@ -109,11 +106,11 @@
*/
private void updateView() {
switch (mAction) {
- case BaseActivity.State.ACTION_OPEN_TREE:
+ case State.ACTION_OPEN_TREE:
mPick.setText(R.string.button_select);
mCancel.setVisibility(View.GONE);
break;
- case BaseActivity.State.ACTION_OPEN_COPY_DESTINATION:
+ case State.ACTION_OPEN_COPY_DESTINATION:
mPick.setText(R.string.button_copy);
mCancel.setVisibility(View.VISIBLE);
break;
@@ -123,7 +120,7 @@
}
if (mPickTarget != null && (
- mAction == BaseActivity.State.ACTION_OPEN_TREE ||
+ mAction == State.ACTION_OPEN_TREE ||
mPickTarget.isCreateSupported())) {
mContainer.setVisibility(View.VISIBLE);
} else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index 4685c41..607cb95 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -16,6 +16,8 @@
package com.android.documentsui;
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.Shared.TAG;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.content.ClipData;
@@ -38,9 +40,6 @@
*/
final class QuickViewIntentBuilder {
- private static final String TAG = "QvIntentBuilder";
- private static final boolean DEBUG = false;
-
private final DocumentInfo mDocument;
private final DocumentContext mContext;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index 1a7095a..c2b64fb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -16,8 +16,9 @@
package com.android.documentsui;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.Shared.TAG;
+import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
import android.app.ActivityManager;
import android.content.AsyncTaskLoader;
@@ -34,7 +35,6 @@
import android.text.format.DateUtils;
import android.util.Log;
-import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.model.RootInfo;
import com.google.common.util.concurrent.AbstractFuture;
@@ -53,8 +53,6 @@
import java.util.concurrent.TimeUnit;
public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
- private static final boolean DEBUG = false;
-
// TODO: clean up cursor ownership so background thread doesn't traverse
// previously returned cursors for filtering/sorting; this currently races
// with the UI thread.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 662822e..cf682fa 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -30,22 +30,21 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils.TruncateAt;
import android.text.style.ImageSpan;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.BaseAdapter;
import android.widget.ImageView;
-import android.widget.ListView;
import android.widget.TextView;
-import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.RecentsProvider.RecentColumns;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
@@ -64,8 +63,7 @@
public class RecentsCreateFragment extends Fragment {
private View mEmptyView;
- private ListView mListView;
-
+ private RecyclerView mRecView;
private DocumentStackAdapter mAdapter;
private LoaderCallbacks<List<DocumentStack>> mCallbacks;
@@ -85,13 +83,14 @@
final View view = inflater.inflate(R.layout.fragment_directory, container, false);
+ mRecView = (RecyclerView) view.findViewById(R.id.recyclerView);
+ mRecView.setLayoutManager(new LinearLayoutManager(getContext()));
+ mRecView.addOnItemTouchListener(mItemListener);
+
mEmptyView = view.findViewById(android.R.id.empty);
- mListView = (ListView) view.findViewById(R.id.list);
- mListView.setOnItemClickListener(mItemListener);
-
mAdapter = new DocumentStackAdapter();
- mListView.setAdapter(mAdapter);
+ mRecView.setAdapter(mAdapter);
final RootsCache roots = DocumentsApplication.getRootsCache(context);
final State state = ((BaseActivity) getActivity()).getDisplayState();
@@ -105,7 +104,7 @@
@Override
public void onLoadFinished(
Loader<List<DocumentStack>> loader, List<DocumentStack> data) {
- mAdapter.swapStacks(data);
+ mAdapter.update(data);
// When launched into empty recents, show drawer
if (mAdapter.isEmpty() && !state.stackTouched &&
@@ -116,7 +115,7 @@
@Override
public void onLoaderReset(Loader<List<DocumentStack>> loader) {
- mAdapter.swapStacks(null);
+ mAdapter.update(null);
}
};
@@ -135,13 +134,24 @@
getLoaderManager().destroyLoader(LOADER_RECENTS);
}
- private OnItemClickListener mItemListener = new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final DocumentStack stack = mAdapter.getItem(position);
- ((BaseActivity) getActivity()).onStackPicked(stack);
- }
- };
+ private RecyclerView.OnItemTouchListener mItemListener =
+ new RecyclerView.OnItemTouchListener() {
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
+ Events.MotionInputEvent event = new Events.MotionInputEvent(e, mRecView);
+ if (event.isOverItem() && event.isActionUp()) {
+ final DocumentStack stack = mAdapter.getItem(event.getItemPosition());
+ ((BaseActivity) getActivity()).onStackPicked(stack);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
+ };
public static class RecentsCreateLoader extends UriDerivativeLoader<Uri, List<DocumentStack>> {
private final RootsCache mRoots;
@@ -187,14 +197,32 @@
}
}
- private class DocumentStackAdapter extends BaseAdapter {
- private List<DocumentStack> mStacks;
+ private static final class StackHolder extends RecyclerView.ViewHolder {
+ public View view;
+ public StackHolder(View view) {
+ super(view);
+ this.view = view;
+ }
+ }
- public DocumentStackAdapter() {
+ private class DocumentStackAdapter extends RecyclerView.Adapter<StackHolder> {
+ @Nullable private List<DocumentStack> mItems;
+
+ DocumentStack getItem(int position) {
+ return mItems.get(position);
}
- public void swapStacks(List<DocumentStack> stacks) {
- mStacks = stacks;
+ @Override
+ public int getItemCount() {
+ return mItems == null ? 0 : mItems.size();
+ }
+
+ boolean isEmpty() {
+ return mItems == null ? true : mItems.isEmpty();
+ }
+
+ void update(@Nullable List<DocumentStack> items) {
+ mItems = items;
if (isEmpty()) {
mEmptyView.setVisibility(View.VISIBLE);
@@ -206,17 +234,22 @@
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final Context context = parent.getContext();
+ public StackHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ final Context context = parent.getContext();
- if (convertView == null) {
- final LayoutInflater inflater = LayoutInflater.from(context);
- convertView = inflater.inflate(R.layout.item_doc_list, parent, false);
- }
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ return new StackHolder(
+ (View) inflater.inflate(R.layout.item_doc_list, parent, false));
+ }
- final ImageView iconMime = (ImageView) convertView.findViewById(R.id.icon_mime);
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- final View line2 = convertView.findViewById(R.id.line2);
+ @Override
+ public void onBindViewHolder(StackHolder holder, int position) {
+ Context context = getContext();
+ View view = holder.view;
+
+ final ImageView iconMime = (ImageView) view.findViewById(R.id.icon_mime);
+ final TextView title = (TextView) view.findViewById(android.R.id.title);
+ final View line2 = view.findViewById(R.id.line2);
final DocumentStack stack = getItem(position);
iconMime.setImageDrawable(stack.root.loadIcon(context));
@@ -234,23 +267,6 @@
title.setEllipsize(TruncateAt.MIDDLE);
if (line2 != null) line2.setVisibility(View.GONE);
-
- return convertView;
- }
-
- @Override
- public int getCount() {
- return mStacks != null ? mStacks.size() : 0;
- }
-
- @Override
- public DocumentStack getItem(int position) {
- return mStacks.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return getItem(position).hashCode();
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index f6e4349..82eb732 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -39,6 +39,7 @@
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
import com.android.internal.util.Predicate;
+
import com.google.android.collect.Sets;
import libcore.io.IoUtils;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 05f7d8d..de35cef 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -16,6 +16,7 @@
package com.android.documentsui;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.Shared.TAG;
import android.content.ContentProviderClient;
@@ -34,12 +35,11 @@
import android.os.SystemClock;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
+import android.support.annotation.VisibleForTesting;
import android.util.Log;
-import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.model.RootInfo;
import com.android.internal.annotations.GuardedBy;
-import android.support.annotation.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
@@ -58,8 +58,6 @@
* Cache of known storage backends and their roots.
*/
public class RootsCache {
- private static final boolean LOGD = false;
-
public static final Uri sNotificationUri = Uri.parse(
"content://com.android.documentsui.roots/");
@@ -91,7 +89,7 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
- if (LOGD) Log.d(TAG, "Updating roots due to change at " + uri);
+ if (DEBUG) Log.d(TAG, "Updating roots due to change at " + uri);
updateAuthorityAsync(uri.getAuthority());
}
}
@@ -148,7 +146,7 @@
final ContentResolver resolver = mContext.getContentResolver();
synchronized (mLock) {
for (String authority : mStoppedAuthorities) {
- if (LOGD) Log.d(TAG, "Loading stopped authority " + authority);
+ if (DEBUG) Log.d(TAG, "Loading stopped authority " + authority);
mRoots.putAll(authority, loadRootsForAuthority(resolver, authority));
}
mStoppedAuthorities.clear();
@@ -199,7 +197,8 @@
}
final long delta = SystemClock.elapsedRealtime() - start;
- Log.d(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
+ if (DEBUG)
+ Log.d(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
synchronized (mLock) {
mRoots = mTaskRoots;
mStoppedAuthorities = mTaskStoppedAuthorities;
@@ -213,7 +212,7 @@
// Ignore stopped packages for now; we might query them
// later during UI interaction.
if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
- if (LOGD) Log.d(TAG, "Ignoring stopped authority " + info.authority);
+ if (DEBUG) Log.d(TAG, "Ignoring stopped authority " + info.authority);
mTaskStoppedAuthorities.add(info.authority);
return;
}
@@ -223,7 +222,7 @@
if (mFilterPackage != null && !mFilterPackage.equals(info.packageName)) {
synchronized (mLock) {
if (mTaskRoots.putAll(info.authority, mRoots.get(info.authority))) {
- if (LOGD) Log.d(TAG, "Used cached roots for " + info.authority);
+ if (DEBUG) Log.d(TAG, "Used cached roots for " + info.authority);
cacheHit = true;
}
}
@@ -241,7 +240,7 @@
* Bring up requested provider and query for all active roots.
*/
private Collection<RootInfo> loadRootsForAuthority(ContentResolver resolver, String authority) {
- if (LOGD) Log.d(TAG, "Loading roots for " + authority);
+ if (DEBUG) Log.d(TAG, "Loading roots for " + authority);
synchronized (mObservedAuthorities) {
if (mObservedAuthorities.add(authority)) {
@@ -370,10 +369,13 @@
// Exclude downloads roots that don't support directory creation
// TODO: Add flag to check the root supports directory creation or not.
if (state.directoryCopy && root.isDownloads()) continue;
- // Only show empty roots when creating
- if ((state.action != State.ACTION_CREATE ||
- state.action != State.ACTION_OPEN_TREE ||
- state.action != State.ACTION_OPEN_COPY_DESTINATION) && empty) continue;
+
+ // Only show empty roots when creating, or in browse mode.
+ if (empty && (state.action == State.ACTION_OPEN
+ || state.action == State.ACTION_GET_CONTENT)) {
+ if (DEBUG) Log.i(TAG, "Skipping empty root: " + root);
+ continue;
+ }
// Only include roots that serve requested content
final boolean overlap =
@@ -385,7 +387,7 @@
// Exclude roots from the calling package.
if (state.excludedAuthorities.contains(root.authority)) {
- if (LOGD) Log.d(TAG, "Excluding root " + root.authority + " from calling package.");
+ if (DEBUG) Log.d(TAG, "Excluding root " + root.authority + " from calling package.");
continue;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index c02184b..c98da47 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -41,7 +41,6 @@
import android.widget.ListView;
import android.widget.TextView;
-import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
index 49651b4..c81377a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
@@ -19,7 +19,6 @@
import android.content.AsyncTaskLoader;
import android.content.Context;
-import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.model.RootInfo;
import java.util.Collection;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index 0c1ebc1..9c884d4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -22,7 +22,7 @@
* @hide
*/
public final class Shared {
- public static final boolean DEBUG = false;
+ public static final boolean DEBUG = true;
public static final String TAG = "Documents";
/**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
index 3ec3d1c..6698ff1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
@@ -16,9 +16,9 @@
package com.android.documentsui;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE;
+import static com.android.documentsui.State.SORT_ORDER_DISPLAY_NAME;
+import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.State.SORT_ORDER_SIZE;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
new file mode 100644
index 0000000..4306a0e
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.DurableUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class State implements android.os.Parcelable {
+ public int action;
+ public String[] acceptMimes;
+
+ /** Explicit user choice */
+ public int userMode = MODE_UNKNOWN;
+ /** Derived after loader */
+ public int derivedMode = MODE_LIST;
+
+ /** Explicit user choice */
+ public int userSortOrder = SORT_ORDER_UNKNOWN;
+ /** Derived after loader */
+ public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
+
+ public boolean allowMultiple;
+ public boolean forceSize;
+ public boolean showSize;
+ public boolean localOnly;
+ public boolean forceAdvanced;
+ public boolean showAdvanced;
+ public boolean stackTouched;
+ public boolean restored;
+ public boolean directoryCopy;
+ /** Transfer mode for file copy/move operations. */
+ public int transferMode;
+
+ /** Current user navigation stack; empty implies recents. */
+ public DocumentStack stack = new DocumentStack();
+ /** Currently active search, overriding any stack. */
+ public String currentSearch;
+
+ /** Instance state for every shown directory */
+ public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>();
+
+ /** Currently copying file */
+ public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<DocumentInfo>();
+
+ /** Name of the package that started DocsUI */
+ public List<String> excludedAuthorities = new ArrayList<>();
+
+ public static final int ACTION_OPEN = 1;
+ public static final int ACTION_CREATE = 2;
+ public static final int ACTION_GET_CONTENT = 3;
+ public static final int ACTION_OPEN_TREE = 4;
+ public static final int ACTION_MANAGE = 5;
+ public static final int ACTION_BROWSE = 6;
+ public static final int ACTION_OPEN_COPY_DESTINATION = 8;
+
+ public static final int MODE_UNKNOWN = 0;
+ public static final int MODE_LIST = 1;
+ public static final int MODE_GRID = 2;
+
+ public static final int SORT_ORDER_UNKNOWN = 0;
+ public static final int SORT_ORDER_DISPLAY_NAME = 1;
+ public static final int SORT_ORDER_LAST_MODIFIED = 2;
+ public static final int SORT_ORDER_SIZE = 3;
+
+ public void initAcceptMimes(Intent intent) {
+ if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
+ acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
+ } else {
+ String glob = intent.getType();
+ acceptMimes = new String[] { glob != null ? glob : "*/*" };
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(action);
+ out.writeInt(userMode);
+ out.writeStringArray(acceptMimes);
+ out.writeInt(userSortOrder);
+ out.writeInt(allowMultiple ? 1 : 0);
+ out.writeInt(forceSize ? 1 : 0);
+ out.writeInt(showSize ? 1 : 0);
+ out.writeInt(localOnly ? 1 : 0);
+ out.writeInt(forceAdvanced ? 1 : 0);
+ out.writeInt(showAdvanced ? 1 : 0);
+ out.writeInt(stackTouched ? 1 : 0);
+ out.writeInt(restored ? 1 : 0);
+ DurableUtils.writeToParcel(out, stack);
+ out.writeString(currentSearch);
+ out.writeMap(dirState);
+ out.writeList(selectedDocumentsForCopy);
+ out.writeList(excludedAuthorities);
+ }
+
+ public static final Creator<State> CREATOR = new Creator<State>() {
+ @Override
+ public State createFromParcel(Parcel in) {
+ final State state = new State();
+ state.action = in.readInt();
+ state.userMode = in.readInt();
+ state.acceptMimes = in.readStringArray();
+ state.userSortOrder = in.readInt();
+ state.allowMultiple = in.readInt() != 0;
+ state.forceSize = in.readInt() != 0;
+ state.showSize = in.readInt() != 0;
+ state.localOnly = in.readInt() != 0;
+ state.forceAdvanced = in.readInt() != 0;
+ state.showAdvanced = in.readInt() != 0;
+ state.stackTouched = in.readInt() != 0;
+ state.restored = in.readInt() != 0;
+ DurableUtils.readFromParcel(in, state.stack);
+ state.currentSearch = in.readString();
+ in.readMap(state.dirState, null);
+ in.readList(state.selectedDocumentsForCopy, null);
+ in.readList(state.excludedAuthorities, null);
+ return state;
+ }
+
+ @Override
+ public State[] newArray(int size) {
+ return new State[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
index 4331b03..1895a6e 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
@@ -22,9 +22,12 @@
import android.database.Cursor;
import android.database.MatrixCursor;
import android.provider.DocumentsContract.Document;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.support.v7.widget.RecyclerView;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
import android.test.mock.MockContentResolver;
+import android.view.ViewGroup;
import com.android.documentsui.DirectoryFragment.Model;
import com.android.documentsui.MultiSelectManager.Selection;
@@ -57,7 +60,9 @@
DirectoryResult r = new DirectoryResult();
r.cursor = cursor;
- model = new Model(mContext, null);
+ // Instantiate the model with a dummy view adapter and listener that (for now) do nothing.
+ model = new Model(mContext, null, new DummyAdapter());
+ model.addUpdateListener(new DummyListener());
model.update(r);
}
@@ -73,8 +78,12 @@
assertEquals(ITEM_COUNT - 2, model.getItemCount());
- // Finalize the deletion
- model.finalizeDeletion(null);
+ // Finalize the deletion. Provide a callback that just ignores errors.
+ model.finalizeDeletion(
+ new Runnable() {
+ @Override
+ public void run() {}
+ });
assertEquals(ITEM_COUNT - 2, model.getItemCount());
}
@@ -154,4 +163,17 @@
}
return model.getDocuments(sel);
}
+
+ private static class DummyListener extends Model.UpdateListener {
+ public void onModelUpdate(Model model) {}
+ public void onModelUpdateFailed(Exception e) {}
+ }
+
+ private static class DummyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ public int getItemCount() { return 0; }
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return null;
+ }
+ }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
index 1325706..7d3498e 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
@@ -19,7 +19,6 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.model.RootInfo;
import com.google.common.collect.Lists;
diff --git a/packages/Keyguard/res/values-nl/strings.xml b/packages/Keyguard/res/values-nl/strings.xml
index 5413895..35caf77 100644
--- a/packages/Keyguard/res/values-nl/strings.xml
+++ b/packages/Keyguard/res/values-nl/strings.xml
@@ -42,7 +42,7 @@
<string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Plaats een simkaart."</string>
<string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"De simkaart ontbreekt of kan niet worden gelezen. Plaats een simkaart."</string>
<string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"Onbruikbare simkaart."</string>
- <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Uw simkaart is permanent uitgeschakeld.\n Neem contact op met uw mobiele serviceprovider voor een nieuwe simkaart."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"Je simkaart is permanent uitgeschakeld.\n Neem contact op met je mobiele serviceprovider voor een nieuwe simkaart."</string>
<string name="keyguard_sim_locked_message" msgid="6875773413306380902">"Simkaart is vergrendeld."</string>
<string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"Simkaart is vergrendeld met PUK-code."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Simkaart ontgrendelen…"</string>
@@ -62,13 +62,13 @@
<string name="kg_wrong_password" msgid="2333281762128113157">"Onjuist wachtwoord"</string>
<string name="kg_wrong_pin" msgid="1131306510833563801">"Onjuiste pincode"</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Probeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw."</string>
- <string name="kg_pattern_instructions" msgid="398978611683075868">"Teken uw patroon"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Teken je patroon"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Geef de pincode van de simkaart op"</string>
<string name="kg_sim_pin_instructions_multi" msgid="7818515973197201434">"Voer de pincode in voor de simkaart van \'<xliff:g id="CARRIER">%1$s</xliff:g>\'"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Pincode opgeven"</string>
<string name="kg_password_instructions" msgid="5753646556186936819">"Wachtwoord invoeren"</string>
<string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"De simkaart is nu uitgeschakeld. Geef de PUK-code op om door te gaan. Neem contact op met de provider voor informatie."</string>
- <string name="kg_puk_enter_puk_hint_multi" msgid="363822494559783025">"Simkaart van \'<xliff:g id="CARRIER">%1$s</xliff:g>\' is nu uitgeschakeld. Voer de PUK-code in om door te gaan. Neem contact op met uw provider voor meer informatie."</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="363822494559783025">"Simkaart van \'<xliff:g id="CARRIER">%1$s</xliff:g>\' is nu uitgeschakeld. Voer de PUK-code in om door te gaan. Neem contact op met je provider voor meer informatie."</string>
<string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Gewenste pincode opgeven"</string>
<string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Gewenste pincode bevestigen"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Simkaart ontgrendelen..."</string>
@@ -77,32 +77,32 @@
<string name="kg_invalid_puk" msgid="3638289409676051243">"Geef de juiste PUK-code opnieuw op. Bij herhaalde pogingen wordt de simkaart permanent uitgeschakeld."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Pincodes komen niet overeen"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Te veel patroonpogingen"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"U heeft uw pincode <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"U heeft uw wachtwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt deze tablet gereset, waardoor alle gegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt deze telefoon gereset, waardoor alle gegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"U heeft <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Deze tablet wordt gereset, waardoor alle gegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_now_wiping" product="default" msgid="7154028908459817066">"U heeft <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Deze telefoon wordt gereset, waardoor alle gegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="6159955099372112688">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt deze gebruiker verwijderd, waardoor alle gebruikersgegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="6945823186629369880">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt deze gebruiker verwijderd, waardoor alle gebruikersgegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="3963486905355778734">"U heeft <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Deze gebruiker wordt verwijderd, waardoor alle gebruikersgegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="7729009752252111673">"U heeft <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Deze gebruiker wordt verwijderd, waardoor alle gebruikersgegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="4621778507387853694">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt het werkprofiel verwijderd, waardoor alle profielgegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt het werkprofiel verwijderd, waardoor alle profielgegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"U heeft <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Het werkprofiel wordt verwijderd, waardoor alle profielgegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"U heeft <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Het werkprofiel wordt verwijderd, waardoor alle profielgegevens worden verwijderd."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd uw tablet te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd uw telefoon te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
- <string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"Onjuiste pincode voor simkaart. U moet nu contact opnemen met uw provider om uw apparaat te ontgrendelen."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Je hebt je pincode <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Je hebt je wachtwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. \n\nProbeer het opnieuw over <xliff:g id="NUMBER_1">%d</xliff:g> seconden."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt deze tablet gereset, waardoor alle gegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt deze telefoon gereset, waardoor alle gegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Deze tablet wordt gereset, waardoor alle gegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="7154028908459817066">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Deze telefoon wordt gereset, waardoor alle gegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="6159955099372112688">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt deze gebruiker verwijderd, waardoor alle gebruikersgegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="6945823186629369880">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt deze gebruiker verwijderd, waardoor alle gebruikersgegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="3963486905355778734">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Deze gebruiker wordt verwijderd, waardoor alle gebruikersgegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="7729009752252111673">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Deze gebruiker wordt verwijderd, waardoor alle gebruikersgegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="4621778507387853694">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt het werkprofiel verwijderd, waardoor alle profielgegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt het werkprofiel verwijderd, waardoor alle profielgegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de tablet te ontgrendelen. Het werkprofiel wordt verwijderd, waardoor alle profielgegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> mislukte pogingen ondernomen om de telefoon te ontgrendelen. Het werkprofiel wordt verwijderd, waardoor alle profielgegevens worden verwijderd."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd je tablet te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd je telefoon te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"Onjuiste pincode voor simkaart. U moet nu contact opnemen met je provider om je apparaat te ontgrendelen."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
- <item quantity="other">Onjuiste pincode voor simkaart. U heeft nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over.</item>
- <item quantity="one">Onjuiste pincode voor simkaart. U heeft nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat u contact met uw provider moet opnemen om uw apparaat te ontgrendelen.</item>
+ <item quantity="other">Onjuiste pincode voor simkaart. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over.</item>
+ <item quantity="one">Onjuiste pincode voor simkaart. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat u contact met je provider moet opnemen om je apparaat te ontgrendelen.</item>
</plurals>
- <string name="kg_password_wrong_puk_code_dead" msgid="7077536808291316208">"Simkaart is onbruikbaar. Neem contact op met uw provider."</string>
+ <string name="kg_password_wrong_puk_code_dead" msgid="7077536808291316208">"Simkaart is onbruikbaar. Neem contact op met je provider."</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="7576227366999858780">
- <item quantity="other">Onjuiste pukcode voor simkaart. U heeft nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over voordat de simkaart definitief onbruikbaar wordt.</item>
- <item quantity="one">Onjuiste pukcode voor simkaart. U heeft nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat de simkaart definitief onbruikbaar wordt.</item>
+ <item quantity="other">Onjuiste pukcode voor simkaart. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over voordat de simkaart definitief onbruikbaar wordt.</item>
+ <item quantity="one">Onjuiste pukcode voor simkaart. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat de simkaart definitief onbruikbaar wordt.</item>
</plurals>
<string name="kg_password_pin_failed" msgid="6268288093558031564">"Bewerking met pincode voor simkaart mislukt."</string>
<string name="kg_password_puk_failed" msgid="2838824369502455984">"Bewerking met pukcode voor simkaart is mislukt."</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
index aeac912..2033159 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
@@ -96,6 +97,12 @@
}
@Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ resetState();
+ }
+
+ @Override
protected int getPromtReasonStringRes(int reason) {
// No message on SIM Pin
return 0;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b752c9b..57ee319 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -121,7 +121,6 @@
private static final int MSG_DEVICE_PROVISIONED = 308;
private static final int MSG_DPM_STATE_CHANGED = 309;
private static final int MSG_USER_SWITCHING = 310;
- private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 311;
private static final int MSG_KEYGUARD_RESET = 312;
private static final int MSG_BOOT_COMPLETED = 313;
private static final int MSG_USER_SWITCH_COMPLETE = 314;
@@ -129,6 +128,7 @@
private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
private static final int MSG_STARTED_WAKING_UP = 319;
private static final int MSG_FINISHED_GOING_TO_SLEEP = 320;
+ private static final int MSG_STARTED_GOING_TO_SLEEP = 321;
private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322;
private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 327;
private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328;
@@ -170,6 +170,7 @@
* until the Keyguard has been dismissed.
*/
private boolean mFingerprintAlreadyAuthenticated;
+ private boolean mGoingToSleep;
private boolean mBouncer;
private boolean mBootCompleted;
@@ -231,9 +232,6 @@
case MSG_USER_SWITCH_COMPLETE:
handleUserSwitchComplete(msg.arg1);
break;
- case MSG_KEYGUARD_VISIBILITY_CHANGED:
- handleKeyguardVisibilityChanged(msg.arg1);
- break;
case MSG_KEYGUARD_RESET:
handleKeyguardReset();
break;
@@ -249,6 +247,9 @@
case MSG_REPORT_EMERGENCY_CALL_ACTION:
handleReportEmergencyCallAction();
break;
+ case MSG_STARTED_GOING_TO_SLEEP:
+ handleStartedGoingToSleep(msg.arg1);
+ break;
case MSG_FINISHED_GOING_TO_SLEEP:
handleFinishedGoingToSleep(msg.arg1);
break;
@@ -884,16 +885,29 @@
}
}
- protected void handleFinishedGoingToSleep(int arg1) {
+ protected void handleStartedGoingToSleep(int arg1) {
clearFingerprintRecognized();
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
+ cb.onStartedGoingToSleep(arg1);
+ }
+ }
+ mGoingToSleep = true;
+ mFingerprintAlreadyAuthenticated = false;
+ updateFingerprintListeningState();
+ }
+
+ protected void handleFinishedGoingToSleep(int arg1) {
+ mGoingToSleep = false;
+ final int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
cb.onFinishedGoingToSleep(arg1);
}
}
- mFingerprintAlreadyAuthenticated = false;
updateFingerprintListeningState();
}
@@ -1032,8 +1046,9 @@
}
private boolean shouldListenForFingerprint() {
- return (mKeyguardIsVisible || !mDeviceInteractive) && !mSwitchingUser
- && !mFingerprintAlreadyAuthenticated && !isFingerprintDisabled(getCurrentUser());
+ return (mKeyguardIsVisible || !mDeviceInteractive || mBouncer || mGoingToSleep)
+ && !mSwitchingUser && !mFingerprintAlreadyAuthenticated
+ && !isFingerprintDisabled(getCurrentUser());
}
private void startListeningForFingerprint() {
@@ -1325,19 +1340,20 @@
}
/**
- * Handle {@link #MSG_KEYGUARD_VISIBILITY_CHANGED}
+ * Notifies that the visibility state of Keyguard has changed.
+ *
+ * <p>Needs to be called from the main thread.
*/
- private void handleKeyguardVisibilityChanged(int showing) {
- if (DEBUG) Log.d(TAG, "handleKeyguardVisibilityChanged(" + showing + ")");
- boolean isShowing = (showing == 1);
- mKeyguardIsVisible = isShowing;
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
+ mKeyguardIsVisible = showing;
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onKeyguardVisibilityChangedRaw(isShowing);
+ cb.onKeyguardVisibilityChangedRaw(showing);
}
}
- if (!isShowing) {
+ if (!showing) {
mFingerprintAlreadyAuthenticated = false;
}
updateFingerprintListeningState();
@@ -1365,6 +1381,7 @@
cb.onKeyguardBouncerChanged(isBouncer);
}
}
+ updateFingerprintListeningState();
}
/**
@@ -1457,13 +1474,6 @@
}
}
- public void sendKeyguardVisibilityChanged(boolean showing) {
- if (DEBUG) Log.d(TAG, "sendKeyguardVisibilityChanged(" + showing + ")");
- Message message = mHandler.obtainMessage(MSG_KEYGUARD_VISIBILITY_CHANGED);
- message.arg1 = showing ? 1 : 0;
- message.sendToTarget();
- }
-
public void sendKeyguardReset() {
mHandler.obtainMessage(MSG_KEYGUARD_RESET).sendToTarget();
}
@@ -1604,6 +1614,10 @@
mHandler.sendEmptyMessage(MSG_STARTED_WAKING_UP);
}
+ public void dispatchStartedGoingToSleep(int why) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_STARTED_GOING_TO_SLEEP, why, 0));
+ }
+
public void dispatchFinishedGoingToSleep(int why) {
synchronized(this) {
mDeviceInteractive = false;
@@ -1629,6 +1643,10 @@
return mDeviceInteractive;
}
+ public boolean isGoingToSleep() {
+ return mGoingToSleep;
+ }
+
/**
* Find the next SubscriptionId for a SIM in the given state, favoring lower slot numbers first.
* @param state
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 15ffe9f..bd6c51c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -153,6 +153,12 @@
public void onStartedWakingUp() { }
/**
+ * Called when the device has started going to sleep.
+ * @param why see {@link #onFinishedGoingToSleep(int)}
+ */
+ public void onStartedGoingToSleep(int why) { }
+
+ /**
* Called when the device has finished going to sleep.
* @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_ADMIN},
* {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER}, or
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index d4c4331..0d4265a 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -91,12 +91,12 @@
return task.createCursor(mResolver, columnNames);
}
- synchronized void clearTasks(int deviceId) {
- mTaskList.clearTaskForDevice(deviceId);
+ synchronized void clearTasks() {
+ mTaskList.clear();
}
synchronized void clearCompletedTasks() {
- mTaskList.clearCompletedTask();
+ mTaskList.clearCompletedTasks();
}
synchronized void clearTask(Identifier parentIdentifier) {
@@ -162,18 +162,7 @@
return null;
}
- void clearTaskForDevice(int deviceId) {
- int i = 0;
- while (i < size()) {
- if (get(i).mIdentifier.mDeviceId == deviceId) {
- remove(i);
- } else {
- i++;
- }
- }
- }
-
- void clearCompletedTask() {
+ void clearCompletedTasks() {
int i = 0;
while (i < size()) {
if (get(i).completed()) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 78ed161..a1a43c1 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -34,6 +34,8 @@
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
/**
* DocumentsProvider for MTP devices.
@@ -56,8 +58,8 @@
private MtpManager mMtpManager;
private ContentResolver mResolver;
- private PipeManager mPipeManager;
- private DocumentLoader mDocumentLoader;
+ private Map<Integer, DeviceToolkit> mDeviceToolkits;
+ private DocumentLoader mDocumentLoaders;
private RootScanner mRootScanner;
/**
@@ -72,8 +74,7 @@
sSingleton = this;
mMtpManager = new MtpManager(getContext());
mResolver = getContext().getContentResolver();
- mPipeManager = new PipeManager();
- mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
+ mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
mRootScanner = new RootScanner(mResolver, mMtpManager);
return true;
}
@@ -82,7 +83,7 @@
void onCreateForTesting(MtpManager mtpManager, ContentResolver resolver) {
mMtpManager = mtpManager;
mResolver = resolver;
- mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
+ mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
mRootScanner = new RootScanner(mResolver, mMtpManager);
}
@@ -158,7 +159,8 @@
}
final Identifier parentIdentifier = Identifier.createFromDocumentId(parentDocumentId);
try {
- return mDocumentLoader.queryChildDocuments(projection, parentIdentifier);
+ return getDocumentLoader(parentIdentifier).queryChildDocuments(
+ projection, parentIdentifier);
} catch (IOException exception) {
throw new FileNotFoundException(exception.getMessage());
}
@@ -172,11 +174,12 @@
try {
switch (mode) {
case "r":
- return mPipeManager.readDocument(mMtpManager, identifier);
+ return getPipeManager(identifier).readDocument(mMtpManager, identifier);
case "w":
// TODO: Clear the parent document loader task (if exists) and call notify
// when writing is completed.
- return mPipeManager.writeDocument(getContext(), mMtpManager, identifier);
+ return getPipeManager(identifier).writeDocument(
+ getContext(), mMtpManager, identifier);
default:
// TODO: Add support for seekable files.
throw new UnsupportedOperationException(
@@ -195,7 +198,7 @@
final Identifier identifier = Identifier.createFromDocumentId(documentId);
try {
return new AssetFileDescriptor(
- mPipeManager.readThumbnail(mMtpManager, identifier),
+ getPipeManager(identifier).readThumbnail(mMtpManager, identifier),
0, // Start offset.
AssetFileDescriptor.UNKNOWN_LENGTH);
} catch (IOException error) {
@@ -212,7 +215,7 @@
mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
final Identifier parentIdentifier = new Identifier(
identifier.mDeviceId, identifier.mStorageId, parentHandle);
- mDocumentLoader.clearTask(parentIdentifier);
+ getDocumentLoader(parentIdentifier).clearTask(parentIdentifier);
notifyChildDocumentsChange(parentIdentifier.toDocumentId());
} catch (IOException error) {
throw new FileNotFoundException(error.getMessage());
@@ -221,7 +224,9 @@
@Override
public void onTrimMemory(int level) {
- mDocumentLoader.clearCompletedTasks();
+ for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
+ toolkit.mDocumentLoader.clearCompletedTasks();
+ }
}
@Override
@@ -241,7 +246,7 @@
.build(), pipe[1]);
final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId,
objectHandle).toDocumentId();
- mDocumentLoader.clearTask(parentId);
+ getDocumentLoader(parentId).clearTask(parentId);
notifyChildDocumentsChange(parentDocumentId);
return documentId;
} catch (IOException error) {
@@ -252,12 +257,15 @@
void openDevice(int deviceId) throws IOException {
mMtpManager.openDevice(deviceId);
+ mDeviceToolkits.put(deviceId, new DeviceToolkit(mMtpManager, mResolver));
mRootScanner.scanNow();
}
void closeDevice(int deviceId) throws IOException {
+ // TODO: Flush the device before closing (if not closed externally).
+ getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
+ mDeviceToolkits.remove(deviceId);
mMtpManager.closeDevice(deviceId);
- mDocumentLoader.clearTasks(deviceId);
mRootScanner.scanNow();
}
@@ -266,7 +274,7 @@
for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
try {
mMtpManager.closeDevice(deviceId);
- mDocumentLoader.clearTasks(deviceId);
+ getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
closed = true;
} catch (IOException d) {
Log.d(TAG, "Failed to close the MTP device: " + deviceId);
@@ -287,4 +295,30 @@
null,
false);
}
+
+ private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException {
+ final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId);
+ if (toolkit == null) {
+ throw new FileNotFoundException();
+ }
+ return toolkit;
+ }
+
+ private PipeManager getPipeManager(Identifier identifier) throws FileNotFoundException {
+ return getDeviceToolkit(identifier.mDeviceId).mPipeManager;
+ }
+
+ private DocumentLoader getDocumentLoader(Identifier identifier) throws FileNotFoundException {
+ return getDeviceToolkit(identifier.mDeviceId).mDocumentLoader;
+ }
+
+ private static class DeviceToolkit {
+ public final PipeManager mPipeManager;
+ public final DocumentLoader mDocumentLoader;
+
+ public DeviceToolkit(MtpManager manager, ContentResolver resolver) {
+ mPipeManager = new PipeManager();
+ mDocumentLoader = new DocumentLoader(manager, resolver);
+ }
+ }
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index ca78a3e..af7f691 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -22,12 +22,14 @@
import android.hardware.usb.UsbManager;
import android.mtp.MtpConstants;
import android.mtp.MtpDevice;
+import android.mtp.MtpEvent;
import android.mtp.MtpObjectInfo;
+import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -42,7 +44,7 @@
private final SparseArray<MtpDevice> mDevices = new SparseArray<>();
MtpManager(Context context) {
- mManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
+ mManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
}
synchronized void openDevice(int deviceId) throws IOException {
@@ -96,81 +98,105 @@
return result;
}
- synchronized MtpRoot[] getRoots(int deviceId) throws IOException {
+ MtpRoot[] getRoots(int deviceId) throws IOException {
final MtpDevice device = getDevice(deviceId);
- final int[] storageIds = device.getStorageIds();
- if (storageIds == null) {
- throw new IOException("Failed to obtain storage IDs.");
+ synchronized (device) {
+ final int[] storageIds = device.getStorageIds();
+ if (storageIds == null) {
+ throw new IOException("Failed to obtain storage IDs.");
+ }
+ final MtpRoot[] results = new MtpRoot[storageIds.length];
+ for (int i = 0; i < storageIds.length; i++) {
+ results[i] = new MtpRoot(deviceId, device.getStorageInfo(storageIds[i]));
+ }
+ return results;
}
- final MtpRoot[] results = new MtpRoot[storageIds.length];
- for (int i = 0; i < storageIds.length; i++) {
- results[i] = new MtpRoot(deviceId, device.getStorageInfo(storageIds[i]));
- }
- return results;
}
- synchronized MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
+ MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
throws IOException {
final MtpDevice device = getDevice(deviceId);
- return device.getObjectInfo(objectHandle);
- }
-
- synchronized int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
- throws IOException {
- final MtpDevice device = getDevice(deviceId);
- return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
- }
-
- synchronized byte[] getObject(int deviceId, int objectHandle, int expectedSize)
- throws IOException {
- final MtpDevice device = getDevice(deviceId);
- return device.getObject(objectHandle, expectedSize);
- }
-
- synchronized byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
- final MtpDevice device = getDevice(deviceId);
- return device.getThumbnail(objectHandle);
- }
-
- synchronized void deleteDocument(int deviceId, int objectHandle) throws IOException {
- final MtpDevice device = getDevice(deviceId);
- if (!device.deleteObject(objectHandle)) {
- throw new IOException("Failed to delete document");
+ synchronized (device) {
+ return device.getObjectInfo(objectHandle);
}
}
- synchronized int createDocument(int deviceId, MtpObjectInfo objectInfo,
- ParcelFileDescriptor source) throws IOException {
+ int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
+ throws IOException {
final MtpDevice device = getDevice(deviceId);
- final MtpObjectInfo sendObjectInfoResult = device.sendObjectInfo(objectInfo);
- if (sendObjectInfoResult == null) {
- throw new IOException("Failed to create a document");
+ synchronized (device) {
+ return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
}
- if (objectInfo.getFormat() != MtpConstants.FORMAT_ASSOCIATION) {
- if (!device.sendObject(sendObjectInfoResult.getObjectHandle(),
- sendObjectInfoResult.getCompressedSize(), source)) {
- throw new IOException("Failed to send contents of a document");
+ }
+
+ byte[] getObject(int deviceId, int objectHandle, int expectedSize)
+ throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ synchronized (device) {
+ return device.getObject(objectHandle, expectedSize);
+ }
+ }
+
+ byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ synchronized (device) {
+ return device.getThumbnail(objectHandle);
+ }
+ }
+
+ void deleteDocument(int deviceId, int objectHandle) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ synchronized (device) {
+ if (!device.deleteObject(objectHandle)) {
+ throw new IOException("Failed to delete document");
}
}
- return sendObjectInfoResult.getObjectHandle();
}
- synchronized int getParent(int deviceId, int objectHandle) throws IOException {
+ int createDocument(int deviceId, MtpObjectInfo objectInfo,
+ ParcelFileDescriptor source) throws IOException {
final MtpDevice device = getDevice(deviceId);
- final int result = (int) device.getParent(objectHandle);
- if (result < 0) {
- throw new FileNotFoundException("Not found parent object");
+ synchronized (device) {
+ final MtpObjectInfo sendObjectInfoResult = device.sendObjectInfo(objectInfo);
+ if (sendObjectInfoResult == null) {
+ throw new IOException("Failed to create a document");
+ }
+ if (objectInfo.getFormat() != MtpConstants.FORMAT_ASSOCIATION) {
+ if (!device.sendObject(sendObjectInfoResult.getObjectHandle(),
+ sendObjectInfoResult.getCompressedSize(), source)) {
+ throw new IOException("Failed to send contents of a document");
+ }
+ }
+ return sendObjectInfoResult.getObjectHandle();
}
- return result;
}
- synchronized void importFile(int deviceId, int objectHandle, ParcelFileDescriptor target)
+ int getParent(int deviceId, int objectHandle) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ synchronized (device) {
+ final int result = (int) device.getParent(objectHandle);
+ if (result < 0) {
+ throw new FileNotFoundException("Not found parent object");
+ }
+ return result;
+ }
+ }
+
+ void importFile(int deviceId, int objectHandle, ParcelFileDescriptor target)
throws IOException {
final MtpDevice device = getDevice(deviceId);
- device.importFile(objectHandle, target);
+ synchronized (device) {
+ device.importFile(objectHandle, target);
+ }
}
- private MtpDevice getDevice(int deviceId) throws IOException {
+ @VisibleForTesting
+ MtpEvent readEvent(int deviceId, CancellationSignal signal) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ return device.readEvent(signal);
+ }
+
+ private synchronized MtpDevice getDevice(int deviceId) throws IOException {
final MtpDevice device = mDevices.get(deviceId);
if (device == null) {
throw new IOException("USB device " + deviceId + " is not opened.");
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
index cd38f1e..affaebd 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
@@ -33,7 +33,7 @@
final ExecutorService mExecutor;
PipeManager() {
- this(Executors.newCachedThreadPool());
+ this(Executors.newSingleThreadExecutor());
}
PipeManager(ExecutorService executor) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 9b316be..4b3a5cd 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -39,7 +39,7 @@
private TestMtpManager mMtpManager;
@Override
- public void setUp() {
+ public void setUp() throws IOException {
mResolver = new TestContentResolver();
mMtpManager = new TestMtpManager(getContext());
mProvider = new MtpDocumentsProvider();
@@ -207,6 +207,8 @@
}
public void testQueryDocument() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(2)
.setFormat(MtpConstants.FORMAT_EXIF_JPEG)
@@ -232,6 +234,8 @@
}
public void testQueryDocument_directory() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(2)
.setFormat(MtpConstants.FORMAT_ASSOCIATION)
@@ -255,6 +259,8 @@
}
public void testQueryDocument_forRoot() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setRoots(0, new MtpRoot[] {
new MtpRoot(
0 /* deviceId */,
@@ -277,6 +283,8 @@
}
public void testQueryChildDocuments() throws Exception {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
@@ -303,6 +311,8 @@
}
public void testQueryChildDocuments_cursorError() throws Exception {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
try {
mProvider.queryChildDocuments("0_0_0", null, null);
fail();
@@ -312,6 +322,8 @@
}
public void testQueryChildDocuments_documentError() throws Exception {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
try {
mProvider.queryChildDocuments("0_0_0", null, null);
@@ -321,7 +333,9 @@
}
}
- public void testDeleteDocument() throws FileNotFoundException {
+ public void testDeleteDocument() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(1)
.setParent(2)
@@ -332,7 +346,9 @@
MtpDocumentsProvider.AUTHORITY, "0_0_2")));
}
- public void testDeleteDocument_error() {
+ public void testDeleteDocument_error() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(2)
.build());
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
index 2c1f115..5547771 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
@@ -16,35 +16,101 @@
package com.android.mtp;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
import android.test.InstrumentationTestCase;
+import java.io.IOException;
import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+@RealDeviceTest
public class MtpManagerTest extends InstrumentationTestCase {
- @RealDeviceTest
- public void testBasic() throws Exception {
- final UsbDevice usbDevice = findDevice();
- final MtpManager manager = new MtpManager(getContext());
- manager.openDevice(usbDevice.getDeviceId());
- waitForStorages(manager, usbDevice.getDeviceId());
- manager.closeDevice(usbDevice.getDeviceId());
+ private static final String ACTION_USB_PERMISSION =
+ "com.android.mtp.USB_PERMISSION";
+ private static final int TIMEOUT_MS = 1000;
+ UsbManager mUsbManager;
+ MtpManager mManager;
+ UsbDevice mUsbDevice;
+ int mRequest;
+
+ @Override
+ public void setUp() throws Exception {
+ mUsbManager = getContext().getSystemService(UsbManager.class);
+ mUsbDevice = findDevice();
+ mManager = new MtpManager(getContext());
+ mManager.openDevice(mUsbDevice.getDeviceId());
+ waitForStorages(mManager, mUsbDevice.getDeviceId());
+ }
+
+ @Override
+ public void tearDown() throws IOException {
+ mManager.closeDevice(mUsbDevice.getDeviceId());
+ }
+
+ public void testCancelEvent() throws Exception {
+ final CancellationSignal signal = new CancellationSignal();
+ final Thread thread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ mManager.readEvent(mUsbDevice.getDeviceId(), signal);
+ } catch (OperationCanceledException | IOException e) {
+ show(e.getMessage());
+ }
+ }
+ };
+ thread.start();
+ Thread.sleep(TIMEOUT_MS);
+ signal.cancel();
+ thread.join(TIMEOUT_MS);
+ }
+
+ private void requestPermission(UsbDevice device) throws InterruptedException {
+ if (mUsbManager.hasPermission(device)) {
+ return;
+ }
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ latch.countDown();
+ getInstrumentation().getTargetContext().unregisterReceiver(this);
+ }
+ };
+ getInstrumentation().getTargetContext().registerReceiver(
+ receiver, new IntentFilter(ACTION_USB_PERMISSION));
+ mUsbManager.requestPermission(device, PendingIntent.getBroadcast(
+ getInstrumentation().getTargetContext(),
+ 0 /* requstCode */,
+ new Intent(ACTION_USB_PERMISSION),
+ 0 /* flags */));
+ latch.await();
+ assertTrue(mUsbManager.hasPermission(device));
}
private UsbDevice findDevice() throws InterruptedException {
- final UsbManager usbManager = getContext().getSystemService(UsbManager.class);
while (true) {
- final HashMap<String,UsbDevice> devices = usbManager.getDeviceList();
+ final HashMap<String,UsbDevice> devices = mUsbManager.getDeviceList();
if (devices.size() == 0) {
show("Wait for devices.");
Thread.sleep(1000);
continue;
}
final UsbDevice device = devices.values().iterator().next();
- final UsbDeviceConnection connection = usbManager.openDevice(device);
+ requestPermission(device);
+ final UsbDeviceConnection connection = mUsbManager.openDevice(device);
+ if (connection == null) {
+ fail("Cannot open USB connection.");
+ }
for (int i = 0; i < device.getInterfaceCount(); i++) {
// Since the test runs real environment, we need to call claim interface with
// force = true to rob interfaces from other applications.
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java
index 9641ad7..22daaf29 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java
@@ -17,7 +17,10 @@
package com.android.mtp;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
@interface RealDeviceTest {}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
index 9824d28..a243375 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
@@ -45,8 +45,11 @@
}
private void show(String tag, Test test, Throwable t) {
+ String message = "";
+ if (t != null && t.getMessage() != null) {
+ message = t.getMessage();
+ }
TestResultActivity.show(
- getContext(),
- String.format("[%s] %s %s", tag, test.toString(), t != null ? t.getMessage() : ""));
+ getContext(), String.format("[%s] %s %s", tag, test.toString(), message));
}
}
diff --git a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
index b5d9138..1530a02 100644
--- a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
+++ b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
@@ -166,7 +166,7 @@
}
}
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
{"nativeReadBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) readBitmapPixels},
{"nativeWriteBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) writeBitmapPixels},
};
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index 8e41a34..f88eca5 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -46,7 +46,7 @@
<string name="savetopdf_button" msgid="2976186791686924743">"PDF sifatida saqlash"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"Chop qilish tanlamalari yoyildi"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"Chop qilish tanlamalari yig‘ildi"</string>
- <string name="search" msgid="5421724265322228497">"Izlash"</string>
+ <string name="search" msgid="5421724265322228497">"Qidirish"</string>
<string name="all_printers_label" msgid="3178848870161526399">"Barcha printerlar"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"Xizmat qo‘shish"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Izlash oynasi ko‘rsatildi"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index f52d755..66233b8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -25,6 +25,7 @@
import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
public class TetherUtil {
@@ -62,6 +63,13 @@
return wifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED;
}
+ private static boolean isEntitlementCheckRequired(Context context) {
+ final CarrierConfigManager configManager = (CarrierConfigManager) context
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ return configManager.getConfig().getBoolean(CarrierConfigManager
+ .KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+ }
+
public static boolean isProvisioningNeeded(Context context) {
// Keep in sync with other usage of config_mobile_hotspot_provision_app.
// ConnectivityManager#enforceTetherChangePermission
@@ -71,6 +79,10 @@
|| provisionApp == null) {
return false;
}
+ // Check carrier config for entitlement checks
+ if (isEntitlementCheckRequired(context) == false) {
+ return false;
+ }
return (provisionApp.length == 2);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 632a867..cff8c23 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -771,6 +771,10 @@
}
}
+ void setRssi(int rssi) {
+ mRssi = rssi;
+ }
+
public static String getSummary(Context context, String ssid, DetailedState state,
boolean isEphemeral, String passpointProvider) {
if (state == DetailedState.CONNECTED && ssid == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index c28288e..d7dfdf8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -312,6 +312,8 @@
connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId());
}
+ final Collection<ScanResult> results = fetchScanResults();
+
final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
if (configs != null) {
mSavedNetworksExist = configs.size() != 0;
@@ -326,8 +328,20 @@
}
}
if (mIncludeSaved) {
- if (!config.isPasspoint() || mIncludePasspoints)
+ if (!config.isPasspoint() || mIncludePasspoints) {
+ // If saved network not present in scan result then set its Rssi to MAX_VALUE
+ boolean apFound = false;
+ for (ScanResult result : results) {
+ if (result.SSID.equals(accessPoint.getSsidStr())) {
+ apFound = true;
+ break;
+ }
+ }
+ if (!apFound) {
+ accessPoint.setRssi(Integer.MAX_VALUE);
+ }
accessPoints.add(accessPoint);
+ }
if (config.isPasspoint() == false) {
apMap.put(accessPoint.getSsidStr(), accessPoint);
@@ -340,7 +354,6 @@
}
}
- final Collection<ScanResult> results = fetchScanResults();
if (results != null) {
for (ScanResult result : results) {
// Ignore hidden and ad-hoc networks.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 1d71346..e12e3a5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -166,7 +166,7 @@
.putExtra(Intent.EXTRA_SETTING_NAME, name)
.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, value)
.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, oldValue);
- context.sendBroadcastAsUser(intent, UserHandle.OWNER, null);
+ context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
}
}
}
diff --git a/packages/Shell/res/values-nl/strings.xml b/packages/Shell/res/values-nl/strings.xml
index 2936387..b0dba4d 100644
--- a/packages/Shell/res/values-nl/strings.xml
+++ b/packages/Shell/res/values-nl/strings.xml
@@ -18,8 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_finished_title" msgid="2293711546892863898">"Foutenrapport vastgelegd"</string>
- <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Veeg naar links om uw bugmelding te delen"</string>
- <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak aan om uw foutenrapport te delen"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Veeg naar links om je bugmelding te delen"</string>
+ <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak aan om je foutenrapport te delen"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Foutenrapporten bevatten gegevens uit de verschillende logbestanden van het systeem, waaronder persoonlijke en privégegevens. Deel foutenrapporten alleen met apps en mensen die u vertrouwt."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Dit bericht de volgende keer weergeven"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Foutenrapporten"</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e4fc075..8fc7ad0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -205,7 +205,8 @@
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
android:screenOrientation="behind"
- android:theme="@style/config_recents_activity_theme">
+ android:resizeableActivity="true"
+ android:theme="@style/RecentsTheme.Wallpaper">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
</intent-filter>
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png
deleted file mode 100644
index 165ef4f..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
deleted file mode 100644
index f95f09f..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
deleted file mode 100644
index 860a906..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
deleted file mode 100644
index bcb203e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index bab268e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png
deleted file mode 100644
index 2f4dbbe..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png
deleted file mode 100644
index d04d84f..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png
deleted file mode 100644
index 1500ae5..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png
deleted file mode 100644
index a7fec49..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png
deleted file mode 100644
index 0feb405..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
deleted file mode 100644
index cabab0d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
deleted file mode 100644
index 16e1bf5..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
deleted file mode 100644
index 94c9743..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index 40375de..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png
deleted file mode 100644
index b7b8f98..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
deleted file mode 100644
index 69b7449..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
deleted file mode 100644
index 57d243c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
deleted file mode 100644
index 8a7ac4f..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index e53eaff..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png
deleted file mode 100644
index 695e7a4..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
deleted file mode 100644
index 88294c0..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
deleted file mode 100644
index 09d684a..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png
deleted file mode 100644
index 62f44e8..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index e31ea32..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png
deleted file mode 100644
index 24f12d7..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png
deleted file mode 100644
index 51482f5..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png
deleted file mode 100644
index 46c7b18..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index 396ad7d..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml
new file mode 100644
index 0000000..f11b690
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF0000FF"
+ android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,10.0l16.0,0.0l0.0,10.0L4.0,20.0L4.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml
new file mode 100644
index 0000000..79ade42
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF0000FF"
+ android:pathData="M24.0,0.0L0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0zM14.0,4.0l0.0,16.0L4.0,20.0L4.0,4.0L14.0,4.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml
new file mode 100644
index 0000000..49c2a38
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF0000FF"
+ android:pathData="M0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0L0.0,24.0zM10.0,20.0L10.0,4.0l10.0,0.0l0.0,16.0L10.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml
new file mode 100644
index 0000000..c3abec2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF0000FF"
+ android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM20.0,14.0L4.0,14.0L4.0,4.0l16.0,0.0L20.0,14.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
index a718d4d..0a4c086 100644
--- a/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
+ android:id="@+id/place_dock_left"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_left" />
+ <Button
+ android:id="@+id/place_dock_right"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_right" />
+ <Button
android:id="@+id/place_left"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
index 250f53d..bf5207a 100644
--- a/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
+ android:id="@+id/place_dock_top"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_top" />
+ <Button
+ android:id="@+id/place_dock_bottom"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_bottom" />
+ <Button
android:id="@+id/place_top"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
index 26c9b1a..6e92afc 100644
--- a/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
+ android:id="@+id/place_dock_left"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_left" />
+ <Button
+ android:id="@+id/place_dock_right"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_right" />
+ <Button
android:id="@+id/place_left"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
index e180daa..faa5f4b 100644
--- a/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
+ android:id="@+id/place_dock_top"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_top" />
+ <Button
+ android:id="@+id/place_dock_bottom"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_dock_bottom" />
+ <Button
android:id="@+id/place_top"
android:layout_width="36dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index c92ba45..d58664f 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -213,7 +213,7 @@
android:layout_width="match_parent"
android:layout_height="40dp"
android:contentDescription="@string/accessibility_menu"
- android:src="@drawable/ic_sysbar_menu_land"
+ android:src="@drawable/ic_sysbar_menu"
android:scaleType="centerInside"
android:layout_gravity="top"
android:visibility="invisible"
@@ -223,7 +223,7 @@
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
android:layout_height="@dimen/navigation_key_width"
android:layout_width="match_parent"
- android:src="@drawable/ic_sysbar_recent_land"
+ android:src="@drawable/ic_sysbar_recent"
android:scaleType="center"
android:layout_weight="0"
android:contentDescription="@string/accessibility_recent"
@@ -237,7 +237,7 @@
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
android:layout_height="@dimen/navigation_key_width"
android:layout_width="match_parent"
- android:src="@drawable/ic_sysbar_home_land"
+ android:src="@drawable/ic_sysbar_home"
android:scaleType="center"
systemui:keyCode="3"
systemui:keyRepeat="false"
@@ -253,7 +253,7 @@
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
android:layout_height="@dimen/navigation_key_width"
android:layout_width="match_parent"
- android:src="@drawable/ic_sysbar_back_land"
+ android:src="@drawable/ic_sysbar_back"
android:scaleType="center"
systemui:keyCode="4"
android:layout_weight="0"
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/res/layout/recents.xml
index 8140dd6..064d225 100644
--- a/packages/SystemUI/res/layout/recents.xml
+++ b/packages/SystemUI/res/layout/recents.xml
@@ -39,12 +39,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <!-- Debug Overlay View -->
- <ViewStub android:id="@+id/debug_overlay_stub"
- android:layout="@layout/recents_debug_overlay"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
<!-- Nav Bar Scrim View -->
<ImageView
android:id="@+id/nav_bar_scrim"
diff --git a/packages/SystemUI/res/layout/recents_debug_overlay.xml b/packages/SystemUI/res/layout/recents_debug_overlay.xml
deleted file mode 100644
index d23495e..0000000
--- a/packages/SystemUI/res/layout/recents_debug_overlay.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.systemui.recents.views.DebugOverlayView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="false">
- <SeekBar
- android:id="@+id/debug_seek_bar_1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:layout_marginTop="25dp" />
- <SeekBar
- android:id="@+id/debug_seek_bar_2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:layout_marginTop="50dp" />
-</com.android.systemui.recents.views.DebugOverlayView>
-
-
diff --git a/packages/DocumentsUI/res/layout/item_loading_list.xml b/packages/SystemUI/res/layout/shelf_menu_anchor.xml
similarity index 62%
rename from packages/DocumentsUI/res/layout/item_loading_list.xml
rename to packages/SystemUI/res/layout/shelf_menu_anchor.xml
index 6f214ed..984f655 100644
--- a/packages/DocumentsUI/res/layout/item_loading_list.xml
+++ b/packages/SystemUI/res/layout/shelf_menu_anchor.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
+<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,17 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/list_item_height">
-
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:indeterminate="true"
- style="?android:attr/progressBarStyle" />
-
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:alpha="0">
+ <ImageView android:id="@+id/shelf_menu_anchor_anchor"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:alpha="0"/>
</FrameLayout>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 7def415f..8b2951a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -354,7 +354,7 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"Vols suprimir l\'usuari?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Totes les aplicacions i les dades d\'aquest usuari se suprimiran."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Suprimeix"</string>
- <string name="battery_saver_notification_title" msgid="237918726750955859">"Estalvi de bateria activada"</string>
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"Estalvi de bateria activat"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"Redueix el rendiment i l\'ús de les dades en segon pla."</string>
<string name="battery_saver_notification_action_text" msgid="109158658238110382">"Desactiva l\'estalvi de bateria"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contingut amagat"</string>
@@ -406,7 +406,7 @@
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> és el diàleg de volum"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Toca per restaurar l\'original."</string>
<string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estàs utilitzant el perfil professional"</string>
- <string name="system_ui_tuner" msgid="708224127392452018">"Configurador de la IU del sistema"</string>
+ <string name="system_ui_tuner" msgid="708224127392452018">"Personalitzador d\'interfície d\'usuari"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostra el percentatge de la bateria inserit"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostra el percentatge del nivell de bateria dins de la icona de la barra d\'estat quan no s\'estigui carregant"</string>
<string name="quick_settings" msgid="10042998191725428">"Configuració ràpida"</string>
@@ -428,12 +428,12 @@
<string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"Zona Wi-Fi"</string>
<string name="accessibility_managed_profile" msgid="6613641363112584120">"Perfil professional"</string>
<string name="tuner_warning_title" msgid="7094689930793031682">"Diversió per a uns quants, però no per a tothom"</string>
- <string name="tuner_warning" msgid="8730648121973575701">"El Configurador de la IU del sistema presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
+ <string name="tuner_warning" msgid="8730648121973575701">"El Personalitzador d\'interfície d\'usuari presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
<string name="tuner_persistent_warning" msgid="8597333795565621795">"És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
<string name="got_it" msgid="2239653834387972602">"D\'acord"</string>
- <string name="tuner_toast" msgid="603429811084428439">"Enhorabona! El Configurador de la IU del sistema s\'ha afegit a Configuració."</string>
+ <string name="tuner_toast" msgid="603429811084428439">"Enhorabona! El Personalitzador d\'interfície d\'usuari s\'ha afegit a Configuració."</string>
<string name="remove_from_settings" msgid="8389591916603406378">"Treu de Configuració"</string>
- <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vols treure el Configurador de la UI del sistema de Configuració i deixar d\'utilitzar-ne totes les funcions?"</string>
+ <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vols suprimir el Personalitzador d\'interfície d\'usuari de Configuració i deixar d\'utilitzar-ne totes les funcions?"</string>
<string name="activity_not_found" msgid="348423244327799974">"L\'aplicació no està instal·lada al dispositiu"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostra els segons del rellotge"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra els segons del rellotge a la barra d\'estat. Això pot afectar la durada de la bateria."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 6f49187..91d975b 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -183,12 +183,12 @@
<string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"Flytilstand er slået til."</string>
<string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Flytilstand er slået fra."</string>
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Flytilstand er slået til."</string>
- <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"\"Vil ikke forstyrres\" er slået til, kun prioritet."</string>
- <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"\"Vil ikke forstyrres\" er slået til, total stilhed."</string>
- <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"\"Vil ikke forstyrres\" er slået til, kun alarmer."</string>
- <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"\"Vil ikke forstyrres\" er slået fra."</string>
- <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"\"Vil ikke forstyrres\" er slået fra."</string>
- <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"\"Vil ikke forstyrres\" er slået til."</string>
+ <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"\"Forstyr ikke\" er slået til, kun prioritet."</string>
+ <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"\"Forstyr ikke\" er slået til, total stilhed."</string>
+ <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"\"Forstyr ikke\" er slået til, kun alarmer."</string>
+ <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"\"Forstyr ikke\" er slået fra."</string>
+ <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"\"Forstyr ikke\" er slået fra."</string>
+ <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"\"Forstyr ikke\" er slået til."</string>
<string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"Bluetooth er slået fra."</string>
<string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"Bluetooth er slået til."</string>
<string name="accessibility_quick_settings_bluetooth_connecting" msgid="6953242966685343855">"Opretter forbindelse til Bluetooth."</string>
@@ -236,7 +236,7 @@
<string name="dessert_case" msgid="1295161776223959221">"Dessertcase"</string>
<string name="start_dreams" msgid="7219575858348719790">"Dagdrøm"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
- <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Vil ikke forstyrres"</string>
+ <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Forstyr ikke"</string>
<string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Kun prioritet"</string>
<string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Kun Alarmer"</string>
<string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Total stilhed"</string>
diff --git a/packages/SystemUI/res/values-en/donottranslate.xml b/packages/SystemUI/res/values-en/donottranslate.xml
new file mode 100644
index 0000000..9f04e1f
--- /dev/null
+++ b/packages/SystemUI/res/values-en/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time_fast</item>
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time_slowly</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index fa01c47..147cbe4 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -34,14 +34,14 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"اعلانها"</string>
<string name="battery_low_title" msgid="6456385927409742437">"شارژ باتری کم است"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده است"</string>
- <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده است. ذخیره کننده باتری روشن است."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده است. بهینهسازی باتری روشن است."</string>
<string name="invalid_charger" msgid="4549105996740522523">"شارژ USB پشتیبانی نمیشود.\nفقط از شارژر ارائه شده استفاده کنید."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"شارژ با USB پشتیبانی نمیشود."</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"فقط از شارژر ارائه شده استفاده کنید."</string>
<string name="battery_low_why" msgid="4553600287639198111">"تنظیمات"</string>
- <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"ذخیرهکننده باتری روشن شود؟"</string>
+ <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"بهینهسازی باتری روشن شود؟"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"روشن کردن"</string>
- <string name="battery_saver_start_action" msgid="5576697451677486320">"ذخیرهکننده باتری را روشن کنید"</string>
+ <string name="battery_saver_start_action" msgid="5576697451677486320">"بهینهسازی باتری را روشن کنید"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"تنظیمات"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"چرخش خودکار صفحه"</string>
@@ -275,7 +275,7 @@
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"برگردان رنگها"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"حالت تصحیح رنگ"</string>
<string name="quick_settings_more_settings" msgid="326112621462813682">"تنظیمات بیشتر"</string>
- <string name="quick_settings_done" msgid="3402999958839153376">"انجام شد"</string>
+ <string name="quick_settings_done" msgid="3402999958839153376">"تمام"</string>
<string name="quick_settings_connected" msgid="1722253542984847487">"متصل"</string>
<string name="quick_settings_connecting" msgid="47623027419264404">"در حال اتصال..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"اتصال به اینترنت با تلفن همراه"</string>
@@ -352,9 +352,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"کاربر حذف شود؟"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"همه برنامهها و دادههای این کاربر حذف میشود."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"حذف"</string>
- <string name="battery_saver_notification_title" msgid="237918726750955859">"ذخیره کننده باتری روشن است."</string>
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"بهینهسازی باتری روشن است."</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"عملکرد و اطلاعات پسزمینه را کاهش میدهد"</string>
- <string name="battery_saver_notification_action_text" msgid="109158658238110382">"خاموش کردن ذخیرهکننده باتری"</string>
+ <string name="battery_saver_notification_action_text" msgid="109158658238110382">"بهینهسازی باتری را خاموش کنید"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"محتواها پنهان هستند"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> شروع به ضبط هر چیزی میکند که در صفحهنمایش شما نمایش داده میشود."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"دوباره نشان داده نشود"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 8ee5f07..0d06636 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -354,7 +354,7 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"Rimuovere l\'utente?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Tutte le app e i dati di questo utente verranno eliminati."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Rimuovi"</string>
- <string name="battery_saver_notification_title" msgid="237918726750955859">"Risparmio batteria attivo"</string>
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"Risparmio energetico attivo"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"Riduce le prestazioni e i dati in background"</string>
<string name="battery_saver_notification_action_text" msgid="109158658238110382">"Disattiva risparmio energetico"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenuti nascosti"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a73f4c1..a034f4f 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -330,7 +330,7 @@
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ユーザーを切り替える"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ユーザーを切り替える、現在のユーザーは<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"現在のユーザー: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
- <string name="accessibility_multi_user_switch_quick_contact" msgid="3020367729287990475">"プロフィールを表示"</string>
+ <string name="accessibility_multi_user_switch_quick_contact" msgid="3020367729287990475">"プロファイルを表示"</string>
<string name="user_add_user" msgid="5110251524486079492">"ユーザーを追加"</string>
<string name="user_new_user_name" msgid="426540612051178753">"新しいユーザー"</string>
<string name="guest_nickname" msgid="8059989128963789678">"ゲスト"</string>
@@ -364,10 +364,10 @@
<string name="media_projection_action_text" msgid="8470872969457985954">"今すぐ開始"</string>
<string name="empty_shade_text" msgid="708135716272867002">"通知はありません"</string>
<string name="device_owned_footer" msgid="3802752663326030053">"端末が監視されている可能性があります"</string>
- <string name="profile_owned_footer" msgid="8021888108553696069">"プロフィールが監視されている可能性があります"</string>
+ <string name="profile_owned_footer" msgid="8021888108553696069">"プロファイルが監視されている可能性があります"</string>
<string name="vpn_footer" msgid="2388611096129106812">"ネットワークが監視されている可能性があります"</string>
<string name="monitoring_title_device_owned" msgid="7121079311903859610">"端末の監視"</string>
- <string name="monitoring_title_profile_owned" msgid="6790109874733501487">"プロフィールの監視"</string>
+ <string name="monitoring_title_profile_owned" msgid="6790109874733501487">"プロファイルの監視"</string>
<string name="monitoring_title" msgid="169206259253048106">"ネットワーク監視"</string>
<string name="disable_vpn" msgid="4435534311510272506">"VPNを無効にする"</string>
<string name="disconnect_vpn" msgid="1324915059568548655">"VPNを切断"</string>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index d608e25..f7e2344 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -20,10 +20,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
- <!-- Whether we're using the tablet-optimized recents interface (we use this
- value at runtime for some things) -->
- <integer name="status_bar_recents_bg_gradient_degrees">90</integer>
-
<!-- The maximum number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">2</integer>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index a9e7735..6ef1ada 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -19,28 +19,6 @@
<!-- thickness (width) of the navigation bar on phones that require it -->
<dimen name="navigation_bar_size">@*android:dimen/navigation_bar_width</dimen>
- <!-- Recent Applications parameters -->
- <!-- How far the thumbnail for a recent app appears from left edge -->
- <dimen name="status_bar_recents_thumbnail_left_margin">0dp</dimen>
- <!-- How far the thumbnail for a recent app appears from top edge -->
- <dimen name="status_bar_recents_thumbnail_top_margin">28dp</dimen>
- <!-- Padding for text descriptions -->
- <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
- <!-- Width of application label text -->
- <dimen name="status_bar_recents_app_label_width">156dip</dimen>
- <!-- Left margin of application label text -->
- <dimen name="status_bar_recents_app_label_left_margin">12dip</dimen>
- <!-- Margin between recents container and glow on the right -->
- <dimen name="status_bar_recents_right_glow_margin">0dip</dimen>
- <!-- Padding between recents items -->
- <dimen name="status_bar_recents_item_padding">2dip</dimen>
- <!-- Where to place the app icon over the thumbnail -->
- <dimen name="status_bar_recents_app_icon_left_margin">8dp</dimen>
- <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
-
- <!-- The side padding for the task stack as a percentage of the width. -->
- <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.26</item>
-
<!-- Standard notification width + gravity -->
<dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen>
<integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 13f02d5..79f3333 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -23,7 +23,7 @@
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wissen"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Verwijderen uit lijst"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App-info"</string>
- <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Uw recente schermen worden hier weergegeven"</string>
+ <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Je recente schermen worden hier weergegeven"</string>
<string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Recente apps negeren"</string>
<plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
<item quantity="other">%d schermen in Overzicht</item>
@@ -71,9 +71,9 @@
<string name="screenshot_saving_title" msgid="8242282144535555697">"Screenshot opslaan..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot wordt opgeslagen."</string>
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot gemaakt."</string>
- <string name="screenshot_saved_text" msgid="1152839647677558815">"Raak aan om uw screenshot te bekijken."</string>
+ <string name="screenshot_saved_text" msgid="1152839647677558815">"Raak aan om je screenshot te bekijken."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot is niet gemaakt."</string>
- <string name="screenshot_failed_text" msgid="1260203058661337274">"Kan geen screenshot maken wegens beperkte opslagruimte of omdat de app of uw organisatie dit niet toestaat."</string>
+ <string name="screenshot_failed_text" msgid="1260203058661337274">"Kan geen screenshot maken wegens beperkte opslagruimte of omdat de app of je organisatie dit niet toestaat."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opties voor USB-bestandsoverdracht"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Koppelen als mediaspeler (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Koppelen als camera (PTP)"</string>
@@ -88,7 +88,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Spraakassistent"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Ontgrendelen"</string>
<string name="accessibility_unlock_button_fingerprint" msgid="8214125623493923751">"Knop Ontgrendelen, wacht op vingerafdruk"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Ontgrendelen zonder uw vingerafdruk te gebruiken"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Ontgrendelen zonder je vingerafdruk te gebruiken"</string>
<string name="unlock_label" msgid="8779712358041029439">"ontgrendelen"</string>
<string name="phone_label" msgid="2320074140205331708">"telefoon openen"</string>
<string name="voice_assist_label" msgid="3956854378310019854">"spraakassistent openen"</string>
@@ -217,7 +217,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data zijn onderbroken"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobiele gegevens zijn onderbroken"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Gegevens zijn onderbroken"</string>
- <string name="data_usage_disabled_dialog" msgid="8453242888903772524">"Omdat de ingestelde gegevenslimiet is bereikt, heeft het apparaat het gegevensverbruik onderbroken voor de rest van deze cyclus.\n\nAls u het gegevensverbruik hervat, kan uw provider kosten in rekening brengen."</string>
+ <string name="data_usage_disabled_dialog" msgid="8453242888903772524">"Omdat de ingestelde gegevenslimiet is bereikt, heeft het apparaat het gegevensverbruik onderbroken voor de rest van deze cyclus.\n\nAls u het gegevensverbruik hervat, kan je provider kosten in rekening brengen."</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Hervatten"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Geen internetverbinding"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Verbonden via wifi"</string>
@@ -289,7 +289,7 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> gebruikt"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limiet van <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Waarschuwing voor <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <string name="recents_empty_message" msgid="8682129509540827999">"Uw recente schermen worden hier weergegeven"</string>
+ <string name="recents_empty_message" msgid="8682129509540827999">"Je recente schermen worden hier weergegeven"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"App-informatie"</string>
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"scherm vastzetten"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string>
@@ -338,7 +338,7 @@
<string name="guest_exit_guest_dialog_message" msgid="4155503224769676625">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
<string name="guest_exit_guest_dialog_remove" msgid="7402231963862520531">"Verwijderen"</string>
<string name="guest_wipe_session_title" msgid="6419439912885956132">"Welkom terug, gast!"</string>
- <string name="guest_wipe_session_message" msgid="8476238178270112811">"Wilt u doorgaan met uw sessie?"</string>
+ <string name="guest_wipe_session_message" msgid="8476238178270112811">"Wilt u doorgaan met je sessie?"</string>
<string name="guest_wipe_session_wipe" msgid="5065558566939858884">"Opnieuw starten"</string>
<string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"Ja, doorgaan"</string>
<string name="guest_notification_title" msgid="1585278533840603063">"Gastgebruiker"</string>
@@ -356,7 +356,7 @@
<string name="battery_saver_notification_text" msgid="820318788126672692">"Vermindert de prestaties en achtergrondgegevens"</string>
<string name="battery_saver_notification_action_text" msgid="109158658238110382">"Accubesparing uitschakelen"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Inhoud verborgen"</string>
- <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> gaat alles vastleggen dat wordt weergegeven op uw scherm."</string>
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> gaat alles vastleggen dat wordt weergegeven op je scherm."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Niet opnieuw weergeven"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Alles wissen"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Nu starten"</string>
@@ -369,16 +369,16 @@
<string name="monitoring_title" msgid="169206259253048106">"Netwerkcontrole"</string>
<string name="disable_vpn" msgid="4435534311510272506">"VPN uitschakelen"</string>
<string name="disconnect_vpn" msgid="1324915059568548655">"Verbinding met VPN verbreken"</string>
- <string name="monitoring_description_device_owned" msgid="5780988291898461883">"Uw apparaat wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nUw beheerder kan instellingen, bedrijfstoegang, apps, gegevens voor uw apparaat en locatiegegevens voor uw apparaat controleren en beheren. Neem voor meer informatie contact op met uw beheerder."</string>
- <string name="monitoring_description_vpn" msgid="4445150119515393526">"U heeft een app toestemming gegeven voor het instellen van een VPN-verbinding.\n\nMet deze app kan uw apparaat- en netwerkactiviteit worden gecontroleerd, inclusief e-mails, apps en websites."</string>
- <string name="monitoring_description_vpn_device_owned" msgid="3090670777499161246">"Uw apparaat wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nUw beheerder kan instellingen, bedrijfstoegang, apps, gegevens voor uw apparaat en locatiegegevens voor uw apparaat controleren en beheren.\n\nU bent verbonden met een VPN, die uw netwerkactiviteit kan controleren, waaronder e-mails, apps en websites.\n\nNeem voor meer informatie contact op met uw beheerder."</string>
- <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Uw werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nUw beheerder kan uw netwerkactiviteit controleren, inclusief e-mails, apps en websites.\n\nNeem contact op met uw beheerder voor meer informatie.\n\nU bent ook verbonden met een VPN waarmee uw netwerkactiviteit kan worden gecontroleerd."</string>
+ <string name="monitoring_description_device_owned" msgid="5780988291898461883">"Je apparaat wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nJe beheerder kan instellingen, bedrijfstoegang, apps, gegevens voor je apparaat en locatiegegevens voor je apparaat controleren en beheren. Neem voor meer informatie contact op met je beheerder."</string>
+ <string name="monitoring_description_vpn" msgid="4445150119515393526">"Je hebt een app toestemming gegeven voor het instellen van een VPN-verbinding.\n\nMet deze app kan je apparaat- en netwerkactiviteit worden gecontroleerd, inclusief e-mails, apps en websites."</string>
+ <string name="monitoring_description_vpn_device_owned" msgid="3090670777499161246">"Je apparaat wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nJe beheerder kan instellingen, bedrijfstoegang, apps, gegevens voor je apparaat en locatiegegevens voor je apparaat controleren en beheren.\n\nU bent verbonden met een VPN, die je netwerkactiviteit kan controleren, waaronder e-mails, apps en websites.\n\nNeem voor meer informatie contact op met je beheerder."</string>
+ <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Je werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nJe beheerder kan je netwerkactiviteit controleren, inclusief e-mails, apps en websites.\n\nNeem contact op met je beheerder voor meer informatie.\n\nU bent ook verbonden met een VPN waarmee je netwerkactiviteit kan worden gecontroleerd."</string>
<string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
- <string name="monitoring_description_app" msgid="6259179342284742878">"U bent verbonden met <xliff:g id="APPLICATION">%1$s</xliff:g>, waarmee uw netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites."</string>
- <string name="monitoring_description_app_personal" msgid="484599052118316268">"U bent verbonden met <xliff:g id="APPLICATION">%1$s</xliff:g>, waarmee uw persoonlijke netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites."</string>
- <string name="monitoring_description_app_work" msgid="1754325860918060897">"Uw werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Deze is verbonden met <xliff:g id="APPLICATION">%2$s</xliff:g>, waarmee uw werkgerelateerde netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites.\n\nNeem contact op met uw beheerder voor meer informatie."</string>
- <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Uw werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Deze is verbonden met <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, waarmee uw werkgerelateerde netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites.\n\nU bent ook verbonden met <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, waarmee uw persoonlijke netwerkactiviteit kan worden gecontroleerd."</string>
- <string name="monitoring_description_vpn_app_device_owned" msgid="4970443827043261703">"Uw apparaat wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nUw beheerder kan instellingen, zakelijke toegang, apps, gekoppelde apparaatgegevens en locatiegegevens voor uw apparaat controleren en beheren.\n\nU bent verbonden met <xliff:g id="APPLICATION">%2$s</xliff:g> waarmee uw netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites.\n\nNeem contact op met uw beheerder voor meer informatie."</string>
+ <string name="monitoring_description_app" msgid="6259179342284742878">"U bent verbonden met <xliff:g id="APPLICATION">%1$s</xliff:g>, waarmee je netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites."</string>
+ <string name="monitoring_description_app_personal" msgid="484599052118316268">"U bent verbonden met <xliff:g id="APPLICATION">%1$s</xliff:g>, waarmee je persoonlijke netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites."</string>
+ <string name="monitoring_description_app_work" msgid="1754325860918060897">"Je werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Deze is verbonden met <xliff:g id="APPLICATION">%2$s</xliff:g>, waarmee je werkgerelateerde netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites.\n\nNeem contact op met je beheerder voor meer informatie."</string>
+ <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Je werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Deze is verbonden met <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, waarmee je werkgerelateerde netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites.\n\nU bent ook verbonden met <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, waarmee je persoonlijke netwerkactiviteit kan worden gecontroleerd."</string>
+ <string name="monitoring_description_vpn_app_device_owned" msgid="4970443827043261703">"Je apparaat wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nJe beheerder kan instellingen, zakelijke toegang, apps, gekoppelde apparaatgegevens en locatiegegevens voor je apparaat controleren en beheren.\n\nU bent verbonden met <xliff:g id="APPLICATION">%2$s</xliff:g> waarmee je netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites.\n\nNeem contact op met je beheerder voor meer informatie."</string>
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Het apparaat blijft vergrendeld totdat u het handmatig ontgrendelt"</string>
<string name="hidden_notifications_title" msgid="7139628534207443290">"Sneller meldingen ontvangen"</string>
<string name="hidden_notifications_text" msgid="2326409389088668981">"Weergeven voordat u ontgrendelt"</string>
@@ -403,7 +403,7 @@
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Afwijzen"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is het volumedialoogvenster"</string>
<string name="volumeui_notification_text" msgid="1826889705095768656">"Tik hierop om het origineel te herstellen."</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"U gebruikt uw werkprofiel"</string>
+ <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"U gebruikt je werkprofiel"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Systeem-UI-tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Percentage ingebouwde accu weergeven"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Accupercentage weergeven in het pictogram op de statusbalk wanneer er niet wordt opgeladen"</string>
@@ -418,8 +418,8 @@
<string name="status_bar_airplane" msgid="7057575501472249002">"Vliegtuigmodus"</string>
<string name="add_tile" msgid="2995389510240786221">"Tegel toevoegen"</string>
<string name="broadcast_tile" msgid="3894036511763289383">"Tegel \'Uitzenden\'"</string>
- <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"U hoort uw volgende alarm niet <xliff:g id="WHEN">%1$s</xliff:g> tenzij u dit voor die tijd uitschakelt"</string>
- <string name="zen_alarm_warning" msgid="444533119582244293">"U hoort uw volgende alarm niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+ <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"U hoort je volgende alarm niet <xliff:g id="WHEN">%1$s</xliff:g> tenzij u dit voor die tijd uitschakelt"</string>
+ <string name="zen_alarm_warning" msgid="444533119582244293">"U hoort je volgende alarm niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="3980063409350522735">"om <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="4242179982586714810">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"Snelle instellingen, <xliff:g id="TITLE">%s</xliff:g>."</string>
@@ -432,7 +432,7 @@
<string name="tuner_toast" msgid="603429811084428439">"Systeem-UI-tuner is toegevoegd aan Instellingen"</string>
<string name="remove_from_settings" msgid="8389591916603406378">"Verwijderen uit Instellingen"</string>
<string name="remove_from_settings_prompt" msgid="6069085993355887748">"Systeem-UI-tuner uit Instellingen verwijderen en het gebruik van alle functies daarvan stopzetten?"</string>
- <string name="activity_not_found" msgid="348423244327799974">"Deze app is niet geïnstalleerd op uw apparaat"</string>
+ <string name="activity_not_found" msgid="348423244327799974">"Deze app is niet geïnstalleerd op je apparaat"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Klokseconden weergeven"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Klokseconden op de statusbalk weergeven. Kan van invloed zijn op de accuduur."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Snelle instellingen opnieuw indelen"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index e3f0fb3..2e41b29 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -36,7 +36,7 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Upozornenia"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Batéria je takmer vybitá"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g>. Úspora batérie je zapnutá."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g>. Šetrič batérie je zapnutý."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Nabíjanie pomocou rozhrania USB nie je podporované.\nPoužívajte iba nabíjačku, ktorá bola dodaná spolu so zariadením."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Nabíjanie prostredníctvom USB nie je podporované."</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"Používajte iba originálnu nabíjačku."</string>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 3a62ad9..f084bc2 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -16,12 +16,6 @@
*/
-->
<resources>
- <!-- Recent Applications parameters -->
- <dimen name="status_bar_recents_app_label_width">190dip</dimen>
-
- <!-- The side padding for the task stack as a percentage of the width. -->
- <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item>
-
<fraction name="keyguard_clock_y_fraction_max">37%</fraction>
<fraction name="keyguard_clock_y_fraction_min">20%</fraction>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 83477c0..4f6d209 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -32,9 +32,4 @@
<!-- Set to true to enable the user switcher on the keyguard. -->
<bool name="config_keyguardUserSwitcher">true</bool>
-
- <!-- Transposes the search bar layout in landscape. -->
- <bool name="recents_has_transposed_search_bar">true</bool>
- <!-- Transposes the nav bar in landscape (only used for purposes of layout). -->
- <bool name="recents_has_transposed_nav_bar">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 1af4ab1..49dbac2 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -32,10 +32,6 @@
<!-- The width of the view containing the menu/ime navigation bar icons -->
<dimen name="navigation_extra_key_width">48dip</dimen>
- <!-- Size of application thumbnail -->
- <dimen name="status_bar_recents_thumbnail_width">200dp</dimen>
- <dimen name="status_bar_recents_thumbnail_height">177dp</dimen>
-
<!-- Minimum fraction of the screen that should be taken up by the notification panel. -->
<item type="dimen" name="notification_panel_min_height_frac">40%</item>
@@ -43,12 +39,6 @@
<!-- On tablets this is just the close_handle_height -->
<dimen name="peek_height">@dimen/close_handle_height</dimen>
- <!-- The side padding for the task stack as a percentage of the width. -->
- <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.075</item>
-
- <!-- The height of the search bar space. -->
- <dimen name="recents_search_bar_space_height">72dp</dimen>
-
<!-- The fraction of the screen height where the clock on the Keyguard has its center. The
max value is used when no notifications are displaying, and the min value is when the
highest possible number of notifications are showing. -->
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index fbeadcd..64e2760e 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -22,27 +22,8 @@
<resources>
<integer name="status_bar_config_maxNotificationIcons">5</integer>
- <!-- Whether we're using the tablet-optimized recents interface (we use this
- value at runtime for some things) -->
- <bool name="config_recents_interface_for_tablets">true</bool>
-
- <!-- Whether recents thumbnails should stretch in both x and y to fill their
- ImageView -->
- <bool name="config_recents_thumbnail_image_fits_to_xy">true</bool>
-
- <!-- Min alpha % that recent items will fade to while being dismissed -->
- <integer name="config_recent_item_min_alpha">0</integer>
-
- <!-- Transposes the search bar layout in landscape -->
- <bool name="recents_transpose_search_layout_with_orientation">false</bool>
-
<!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
card. -->
<integer name="keyguard_max_notification_count">5</integer>
-
- <!-- Transposes the search bar layout in landscape. -->
- <bool name="recents_has_transposed_search_bar">false</bool>
- <!-- Transposes the nav bar in landscape (only used for purposes of layout). -->
- <bool name="recents_has_transposed_nav_bar">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index dd158c2..7cee381 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -29,42 +29,9 @@
<!-- Bottom margin (from display edge) for status bar panels -->
<dimen name="panel_float">56dp</dimen>
- <!-- Recent Applications parameters -->
- <!-- How far the thumbnail for a recent app appears from left edge -->
- <dimen name="status_bar_recents_thumbnail_left_margin">28dp</dimen>
- <!-- Upper width limit for application icon -->
- <dimen name="status_bar_recents_app_icon_max_width">64dp</dimen>
- <!-- Upper height limit for application icon -->
- <dimen name="status_bar_recents_app_icon_max_height">64dp</dimen>
-
- <!-- Size of application icon -->
- <dimen name="status_bar_recents_thumbnail_width">208dp</dimen>
- <dimen name="status_bar_recents_thumbnail_height">130dp</dimen>
-
- <!-- Width of recents panel -->
- <dimen name="status_bar_recents_width">600dp</dimen>
- <!-- Padding for text descriptions -->
- <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
- <!-- Size of application label text -->
- <dimen name="status_bar_recents_app_label_text_size">18dip</dimen>
- <!-- Size of application description text -->
- <dimen name="status_bar_recents_app_description_text_size">18dip</dimen>
- <!-- Width of application label text -->
- <dimen name="status_bar_recents_app_label_width">97dip</dimen>
- <!-- Left margin for application label -->
- <dimen name="status_bar_recents_app_label_left_margin">16dip</dimen>
- <!-- Size of fading edge for text -->
- <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
- <!-- Size of fading edge for scrolling -->
- <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
-
<!-- The radius of the rounded corners on a task view. -->
<dimen name="recents_task_view_rounded_corners_radius">3dp</dimen>
- <!-- Where to place the app icon over the thumbnail -->
- <dimen name="status_bar_recents_app_icon_left_margin">0dp</dimen>
- <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
-
<!-- The fraction of the screen height where the clock on the Keyguard has its center. The
max value is used when no notifications are displaying, and the min value is when the
highest possible number of notifications are showing. -->
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index fdad0d5..49edb57 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -203,10 +203,10 @@
<string name="accessibility_quick_settings_close" msgid="3115847794692516306">"Paneli kapat."</string>
<string name="accessibility_quick_settings_more_time" msgid="3659274935356197708">"Daha uzun süre."</string>
<string name="accessibility_quick_settings_less_time" msgid="2404728746293515623">"Daha kısa süre."</string>
- <string name="accessibility_quick_settings_flashlight_off" msgid="4936432000069786988">"Flaş ışığı kapalı."</string>
- <string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"Flaş ışığı açık."</string>
- <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"Flaş ışığı kapatıldı."</string>
- <string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"Flaş ışığı açıldı."</string>
+ <string name="accessibility_quick_settings_flashlight_off" msgid="4936432000069786988">"El feneri kapalı."</string>
+ <string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"El feneri açık."</string>
+ <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"El feneri kapatıldı."</string>
+ <string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"El feneri açıldı."</string>
<string name="accessibility_quick_settings_color_inversion_changed_off" msgid="4406577213290173911">"Renkleri ters çevirme işlevi kapatıldı."</string>
<string name="accessibility_quick_settings_color_inversion_changed_on" msgid="6897462320184911126">"Renkleri ters çevirme işlevi açıldı."</string>
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="5004708003447561394">"Mobil hotspot kapatıldı."</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 04ec78b..9f9fe5f 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -82,7 +82,7 @@
<string name="accessibility_home" msgid="8217216074895377641">"Uyga"</string>
<string name="accessibility_menu" msgid="316839303324695949">"Menyu"</string>
<string name="accessibility_recent" msgid="5208608566793607626">"Umumiy nazar"</string>
- <string name="accessibility_search_light" msgid="1103867596330271848">"Izlash"</string>
+ <string name="accessibility_search_light" msgid="1103867596330271848">"Qidirish"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
<string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Ovozli yordam"</string>
@@ -303,7 +303,7 @@
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g>da to‘ladi"</string>
<string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"Quvvat olmayapti"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Tarmoq nazorat\nostida bo‘lishi mumkin"</string>
- <string name="description_target_search" msgid="3091587249776033139">"Izlash"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Qidirish"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun yuqoriga suring."</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun chapga suring."</string>
<string name="zen_priority_introduction" msgid="3070506961866919502">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq uyg‘otkich signallari, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan qo‘ng‘iroqlar bundan mustasno."</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index da21084..f40f0d9 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -24,11 +24,8 @@
<color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
<color name="system_bar_background_transparent">#00000000</color>
<color name="notification_panel_solid_background">#ff000000</color>
- <drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable>
- <color name="status_bar_recents_app_label_color">#ffffffff</color>
<drawable name="status_bar_notification_row_background_color">#ff090909</drawable>
<color name="notification_list_shadow_top">#80000000</color>
- <drawable name="recents_callout_line">#99ffffff</drawable>
<drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
<color name="batterymeter_frame_color">#4DFFFFFF</color><!-- 30% white -->
<color name="batterymeter_charge_color">#FFFFFFFF</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 8da1148..1d19589 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -20,15 +20,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
-
- <!-- Whether we're using the tablet-optimized recents interface (we use this
- value at runtime for some things) -->
- <bool name="config_recents_interface_for_tablets">false</bool>
-
- <!-- Whether recents thumbnails should stretch in both x and y to fill their
- ImageView -->
- <bool name="config_recents_thumbnail_image_fits_to_xy">false</bool>
-
<!-- Whether recents should use hardware layers for its taskviews. This flag can be enabled
for devices where the java drawing of round rects may be slow -->
<bool name="config_recents_use_hardware_layers">false</bool>
@@ -46,9 +37,6 @@
certain GPU's and thus can be turned off with only minimal visual impact. -->
<bool name="config_notifications_round_rect_clipping">true</bool>
- <!-- The theme to use for RecentsActivity. -->
- <item type="style" name="config_recents_activity_theme">@style/RecentsTheme.Wallpaper</item>
-
<!-- Control whether status bar should distinguish HSPA data icon form UMTS
data icon on devices -->
<bool name="config_hspa_data_distinguishable">false</bool>
@@ -92,10 +80,6 @@
<!-- The length of the vibration when the notification pops open. -->
<integer name="one_finger_pop_duration_ms">10</integer>
- <!-- Whether we're using the tablet-optimized recents interface (we use this
- value at runtime for some things) -->
- <integer name="status_bar_recents_bg_gradient_degrees">90</integer>
-
<!-- decay duration (from size_max -> size), in ms -->
<integer name="navigation_bar_deadzone_hold">333</integer>
<integer name="navigation_bar_deadzone_decay">333</integer>
@@ -205,12 +189,6 @@
<!-- The delay to enforce between each alt-tab key press. -->
<integer name="recents_alt_tab_key_delay">200</integer>
- <!-- Transposes the search bar layout in landscape. -->
- <bool name="recents_has_transposed_search_bar">true</bool>
-
- <!-- Transposes the nav bar in landscape (only used for purposes of layout). -->
- <bool name="recents_has_transposed_nav_bar">true</bool>
-
<!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
<integer name="recents_svelte_level">0</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 96a77256..abfd863 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -16,45 +16,6 @@
*/
-->
<resources>
- <!-- Recent Applications parameters -->
- <!-- Upper width limit for application icon -->
- <dimen name="status_bar_recents_app_icon_max_width">48dp</dimen>
- <!-- Upper height limit for application icon -->
- <dimen name="status_bar_recents_app_icon_max_height">48dp</dimen>
-
- <!-- Size of application thumbnail -->
- <dimen name="status_bar_recents_thumbnail_width">164dp</dimen>
- <dimen name="status_bar_recents_thumbnail_height">145dp</dimen>
- <dimen name="status_bar_recents_thumbnail_bg_padding">4dp</dimen>
-
- <!-- Size of application label text -->
- <dimen name="status_bar_recents_app_label_text_size">14dip</dimen>
- <!-- Size of application description text -->
- <dimen name="status_bar_recents_app_description_text_size">14dip</dimen>
- <!-- Size of fading edge for text -->
- <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
- <!-- Size of fading edge for scrolling -->
- <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
- <!-- Margin between recents container and glow on the right -->
- <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
- <!-- How far the thumbnail for a recent app appears from left edge -->
- <dimen name="status_bar_recents_thumbnail_left_margin">20dp</dimen>
- <!-- Padding for text descriptions -->
- <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
- <!-- Width of application label text -->
- <dimen name="status_bar_recents_app_label_width">88dip</dimen>
- <!-- Left margin of application label text -->
- <dimen name="status_bar_recents_app_label_left_margin">0dip</dimen>
- <!-- Padding between recents items -->
- <dimen name="status_bar_recents_item_padding">0dip</dimen>
- <!-- When recents first appears, how far the icon and label of the primary activity
- travel -->
- <dimen name="status_bar_recents_app_icon_translate_distance">35dip</dimen>
-
- <!-- Where to place the app icon over the thumbnail -->
- <dimen name="status_bar_recents_app_icon_left_margin">0dp</dimen>
- <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
-
<!-- Amount to offset bottom of notification peek window from top of status bar. -->
<dimen name="peek_window_y_offset">-12dp</dimen>
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index 351a1fd..30ff704 100644
--- a/packages/SystemUI/res/values/donottranslate.xml
+++ b/packages/SystemUI/res/values/donottranslate.xml
@@ -20,4 +20,10 @@
<!-- Date format for display: should match the lockscreen in /policy. -->
<string name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</string>
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time</item>
+
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time</item>
+
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index db1e688..d567e97 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -793,6 +793,12 @@
<!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]-->
<string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+ <!-- Indication on the keyguard that is shown when the device is charging rapidly. Should match keyguard_plugged_in_charging_fast [CHAR LIMIT=40]-->
+ <string name="keyguard_indication_charging_time_fast">Charging rapidly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
+ <!-- Indication on the keyguard that is shown when the device is charging slowly. Should match keyguard_plugged_in_charging_slowly [CHAR LIMIT=40]-->
+ <string name="keyguard_indication_charging_time_slowly">Charging slowly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
<!-- Related to user switcher --><skip/>
<!-- Accessibility label for the button that opens the user switcher. -->
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index dd933898..1d0bfe7 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -20,10 +20,6 @@
import android.view.View;
public interface RecentsComponent {
- public interface Callbacks {
- public void onVisibilityChanged(boolean visible);
- }
-
void showRecents(boolean triggeredFromAltTab, View statusBarView);
void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
void toggleRecents(Display display, int layoutDirection, View statusBarView);
@@ -31,5 +27,4 @@
void cancelPreloadingRecents();
void showNextAffiliatedTask();
void showPrevAffiliatedTask();
- void setCallback(Callbacks cb);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 33f6564..6207324 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -34,6 +34,8 @@
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
+import com.android.systemui.classifier.FalsingManager;
+
public class SwipeHelper implements Gefingerpoken {
static final String TAG = "com.android.systemui.SwipeHelper";
private static final boolean DEBUG = false;
@@ -67,6 +69,7 @@
private Handler mHandler;
private int mSwipeDirection;
private VelocityTracker mVelocityTracker;
+ private FalsingManager mFalsingManager;
private float mInitialTouchPos;
private boolean mDragging;
@@ -97,6 +100,7 @@
android.R.interpolator.fast_out_linear_in);
mFalsingThreshold = context.getResources().getDimensionPixelSize(
R.dimen.swipe_helper_falsing_threshold);
+ mFalsingManager = FalsingManager.getInstance(context);
}
public void setLongPressListener(LongPressListener listener) {
@@ -449,8 +453,13 @@
boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
(Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
(velocity > 0) == (getTranslation(mCurrAnimView) > 0);
- boolean falsingDetected = mCallback.isAntiFalsingNeeded()
- && !mTouchAboveFalsingThreshold;
+ boolean falsingDetected = mCallback.isAntiFalsingNeeded();
+
+ if (mFalsingManager.isClassiferEnabled()) {
+ falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
+ } else {
+ falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
+ }
boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
&& !falsingDetected && (childSwipedFastEnough || childSwipedFarEnough)
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 33bd726..5bf251b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -22,7 +22,9 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
+import android.os.Process;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.util.Log;
import java.util.HashMap;
@@ -51,12 +53,20 @@
};
/**
+ * The classes of the stuff to start for each user. This is a subset of the services listed
+ * above.
+ */
+ private final Class<?>[] SERVICES_PER_USER = new Class[] {
+ com.android.systemui.recents.Recents.class
+ };
+
+ /**
* Hold a reference on the stuff we start.
*/
private final SystemUI[] mServices = new SystemUI[SERVICES.length];
private boolean mServicesStarted;
private boolean mBootCompleted;
- private final Map<Class<?>, Object> mComponents = new HashMap<Class<?>, Object>();
+ private final Map<Class<?>, Object> mComponents = new HashMap<>();
@Override
public void onCreate() {
@@ -66,24 +76,32 @@
// the theme set there.
setTheme(R.style.systemui_theme);
- IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mBootCompleted) return;
+ if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mBootCompleted) return;
- if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
- unregisterReceiver(this);
- mBootCompleted = true;
- if (mServicesStarted) {
- final int N = mServices.length;
- for (int i = 0; i < N; i++) {
- mServices[i].onBootCompleted();
+ if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
+ unregisterReceiver(this);
+ mBootCompleted = true;
+ if (mServicesStarted) {
+ final int N = mServices.length;
+ for (int i = 0; i < N; i++) {
+ mServices[i].onBootCompleted();
+ }
}
}
- }
- }, filter);
+ }, filter);
+ } else {
+ // For a secondary user, boot-completed will never be called because it has already
+ // been broadcasted on startup for the primary SystemUI process. Instead, for
+ // components which require the SystemUI component to be initialized per-user, we
+ // start those components now for the current non-system user.
+ startServicesIfNeeded(SERVICES_PER_USER);
+ }
}
/**
@@ -94,6 +112,10 @@
* <p>This method must only be called from the main thread.</p>
*/
public void startServicesIfNeeded() {
+ startServicesIfNeeded(SERVICES);
+ }
+
+ private void startServicesIfNeeded(Class<?>[] services) {
if (mServicesStarted) {
return;
}
@@ -107,13 +129,14 @@
}
}
- Log.v(TAG, "Starting SystemUI services.");
- final int N = SERVICES.length;
+ Log.v(TAG, "Starting SystemUI services for user " +
+ Process.myUserHandle().getIdentifier() + ".");
+ final int N = services.length;
for (int i=0; i<N; i++) {
- Class<?> cl = SERVICES[i];
+ Class<?> cl = services[i];
if (DEBUG) Log.d(TAG, "loading: " + cl);
try {
- mServices[i] = (SystemUI)cl.newInstance();
+ mServices[i] = (SystemUI) cl.newInstance();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
@@ -136,7 +159,9 @@
if (mServicesStarted) {
int len = mServices.length;
for (int i = 0; i < len; i++) {
- mServices[i].onConfigurationChanged(newConfig);
+ if (mServices[i] != null) {
+ mServices[i].onConfigurationChanged(newConfig);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java
rename to packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
index e458862..91f6520 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
@@ -21,7 +21,6 @@
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
@@ -30,8 +29,6 @@
import android.util.Log;
import android.view.MotionEvent;
-import com.android.systemui.statusbar.StatusBarState;
-
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -47,24 +44,29 @@
* A session starts when the screen is turned on.
* A session ends when the screen is turned off or user unlocks the phone.
*/
-public class LockedPhoneAnalytics implements SensorEventListener {
- private static final String TAG = "LockedPhoneAnalytics";
- private static final String ANALYTICS_ENABLE = "locked_phone_analytics_enable";
- private static final String ENFORCE_BOUNCER = "locked_phone_analytics_enforce_bouncer";
- private static final String COLLECT_BAD_TOCUHES = "locked_phone_analytics_collect_bad_touches";
+public class DataCollector implements SensorEventListener {
+ private static final String TAG = "DataCollector";
+ private static final String COLLECTOR_ENABLE = "data_collector_enable";
+ private static final String COLLECT_BAD_TOUCHES = "data_collector_collect_bad_touches";
private static final long TIMEOUT_MILLIS = 11000; // 11 seconds.
public static final boolean DEBUG = false;
- private static final int[] SENSORS = new int[] {
- Sensor.TYPE_ACCELEROMETER,
- Sensor.TYPE_GYROSCOPE,
- Sensor.TYPE_PROXIMITY,
- Sensor.TYPE_LIGHT,
- Sensor.TYPE_ROTATION_VECTOR,
- };
-
private final Handler mHandler = new Handler();
+ private final Context mContext;
+
+ // Err on the side of caution, so logging is not started after a crash even tough the screen
+ // is off.
+ private SensorLoggerSession mCurrentSession = null;
+
+ private boolean mEnableCollector = false;
+ private boolean mTimeoutActive = false;
+ private boolean mCollectBadTouches = false;
+ private boolean mCornerSwiping = false;
+ private boolean mTrackingStarted = false;
+
+ private static DataCollector sInstance = null;
+
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -72,69 +74,40 @@
}
};
- private final SensorManager mSensorManager;
- private final Context mContext;
-
- // Err on the side of caution, so logging is not started after a crash even tough the screen
- // is off.
- private SensorLoggerSession mCurrentSession = null;
-
- private boolean mEnableAnalytics = false;
- private boolean mEnforceBouncer = false;
- private boolean mTimeoutActive = false;
- private boolean mCollectBadTouches = false;
- private boolean mBouncerOn = false;
- private boolean mCornerSwiping = false;
- private boolean mTrackingStarted = false;
-
- private int mState = StatusBarState.SHADE;
-
- private static LockedPhoneAnalytics sInstance = null;
-
- private LockedPhoneAnalytics(Context context) {
- mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ private DataCollector(Context context) {
mContext = context;
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(ANALYTICS_ENABLE), false,
+ Settings.Secure.getUriFor(COLLECTOR_ENABLE), false,
mSettingsObserver,
UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
- mSettingsObserver,
- UserHandle.USER_ALL);
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(COLLECT_BAD_TOCUHES), false,
+ Settings.Secure.getUriFor(COLLECT_BAD_TOUCHES), false,
mSettingsObserver,
UserHandle.USER_ALL);
updateConfiguration();
}
- public static LockedPhoneAnalytics getInstance(Context context) {
+ public static DataCollector getInstance(Context context) {
if (sInstance == null) {
- sInstance = new LockedPhoneAnalytics(context);
+ sInstance = new DataCollector(context);
}
return sInstance;
}
private void updateConfiguration() {
- mEnableAnalytics = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
+ mEnableCollector = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
mContext.getContentResolver(),
- ANALYTICS_ENABLE, 0);
- mEnforceBouncer = mEnableAnalytics && 0 != Settings.Secure.getInt(
+ COLLECTOR_ENABLE, 0);
+ mCollectBadTouches = mEnableCollector && 0 != Settings.Secure.getInt(
mContext.getContentResolver(),
- ENFORCE_BOUNCER, 0);
- mCollectBadTouches = mEnableAnalytics && 0 != Settings.Secure.getInt(
- mContext.getContentResolver(),
- COLLECT_BAD_TOCUHES, 0);
+ COLLECT_BAD_TOUCHES, 0);
}
private boolean sessionEntrypoint() {
- if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
- && mEnableAnalytics && mCurrentSession == null) {
+ if (mEnableCollector && mCurrentSession == null) {
onSessionStart();
return true;
}
@@ -142,22 +115,15 @@
}
private void sessionExitpoint(int result) {
- if (mEnableAnalytics && mCurrentSession != null) {
+ if (mEnableCollector && mCurrentSession != null) {
onSessionEnd(result);
}
}
private void onSessionStart() {
- mBouncerOn = false;
mCornerSwiping = false;
mTrackingStarted = false;
mCurrentSession = new SensorLoggerSession(System.currentTimeMillis(), System.nanoTime());
- for (int sensorType : SENSORS) {
- Sensor s = mSensorManager.getDefaultSensor(sensorType);
- if (s != null) {
- mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
- }
- }
}
private void onSessionEnd(int result) {
@@ -196,10 +162,9 @@
});
}
-
@Override
public synchronized void onSensorChanged(SensorEvent event) {
- if (mEnableAnalytics && mCurrentSession != null) {
+ if (mEnableCollector && mCurrentSession != null) {
mCurrentSession.addSensorEvent(event, System.nanoTime());
enforceTimeout();
}
@@ -221,18 +186,14 @@
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
- public boolean shouldEnforceBouncer() {
- return mEnforceBouncer;
+ public boolean isEnabled() {
+ return mEnableCollector;
}
- public void setStatusBarState(int state) {
- mState = state;
- }
-
- public void onScreenOn() {
+ public void onScreenTurningOn() {
if (sessionEntrypoint()) {
if (DEBUG) {
- Log.d(TAG, "onScreenOn");
+ Log.d(TAG, "onScreenTurningOn");
}
addEvent(PhoneEvent.ON_SCREEN_ON);
}
@@ -264,23 +225,17 @@
}
public void onBouncerShown() {
- if (!mBouncerOn) {
- if (DEBUG) {
- Log.d(TAG, "onBouncerShown");
- }
- mBouncerOn = true;
- addEvent(PhoneEvent.ON_BOUNCER_SHOWN);
+ if (DEBUG) {
+ Log.d(TAG, "onBouncerShown");
}
+ addEvent(PhoneEvent.ON_BOUNCER_SHOWN);
}
public void onBouncerHidden() {
- if (mBouncerOn) {
- if (DEBUG) {
- Log.d(TAG, "onBouncerHidden");
- }
- mBouncerOn = false;
- addEvent(PhoneEvent.ON_BOUNCER_HIDDEN);
+ if (DEBUG) {
+ Log.d(TAG, "onBouncerHidden");
}
+ addEvent(PhoneEvent.ON_BOUNCER_HIDDEN);
}
public void onQsDown() {
@@ -433,20 +388,20 @@
addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_HINT_STARTED);
}
- public void onTouchEvent(MotionEvent ev, int width, int height) {
- if (!mBouncerOn && mCurrentSession != null) {
+ public void onTouchEvent(MotionEvent event, int width, int height) {
+ if (mCurrentSession != null) {
if (DEBUG) {
Log.v(TAG, "onTouchEvent(ev.action="
- + MotionEvent.actionToString(ev.getAction()) + ")");
+ + MotionEvent.actionToString(event.getAction()) + ")");
}
- mCurrentSession.addMotionEvent(ev);
+ mCurrentSession.addMotionEvent(event);
mCurrentSession.setTouchArea(width, height);
enforceTimeout();
}
}
private void addEvent(int eventType) {
- if (mEnableAnalytics && mCurrentSession != null) {
+ if (mEnableCollector && mCurrentSession != null) {
mCurrentSession.addPhoneEvent(eventType, System.nanoTime());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
index 09383f6a..0e28002 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
@@ -57,7 +57,7 @@
mResult = result;
mEndTimestampMillis = endTimestampMillis;
- if (LockedPhoneAnalytics.DEBUG) {
+ if (DataCollector.DEBUG) {
Log.d(TAG, "Ending session result=" + result + " it lasted for " +
(float) (mEndTimestampMillis - mStartTimestampMillis) / 1000f + "s");
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 9265b63..6f26222 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -1,5 +1,7 @@
package com.android.systemui.assist;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.SearchManager;
@@ -9,9 +11,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.graphics.PixelFormat;
-import android.media.AudioAttributes;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -21,7 +21,6 @@
import android.service.voice.VoiceInteractionSession;
import android.util.Log;
import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -33,10 +32,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
/**
* Class to manage everything related to assist in SystemUI.
@@ -58,8 +53,6 @@
private final BaseStatusBar mBar;
private final AssistUtils mAssistUtils;
- private ComponentName mAssistComponent;
-
private IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -82,23 +75,11 @@
}
};
- private final ContentObserver mAssistSettingsObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- updateAssistInfo();
- }
- };
-
public AssistManager(BaseStatusBar bar, Context context) {
mContext = context;
mBar = bar;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mAssistUtils = new AssistUtils(context);
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSISTANT), false,
- mAssistSettingsObserver);
- mAssistSettingsObserver.onChange(false);
mAssistDisclosure = new AssistDisclosure(context, new Handler());
}
@@ -123,19 +104,19 @@
}
public void startAssist(Bundle args) {
- updateAssistInfo();
- if (mAssistComponent == null) {
+ final ComponentName assistComponent = getAssistInfo();
+ if (assistComponent == null) {
return;
}
- final boolean isService = isAssistantService();
+ final boolean isService = assistComponent.equals(getVoiceInteractorComponentName());
if (!isService || !isVoiceSessionRunning()) {
- showOrb();
+ showOrb(assistComponent, isService);
mView.postDelayed(mHideRunnable, isService
? TIMEOUT_SERVICE
: TIMEOUT_ACTIVITY);
}
- startAssistInternal(args);
+ startAssistInternal(args, assistComponent, isService);
}
public void hideAssist() {
@@ -161,22 +142,21 @@
return lp;
}
- private void showOrb() {
- maybeSwapSearchIcon();
+ private void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
+ maybeSwapSearchIcon(assistComponent, isService);
mView.show(true /* show */, true /* animate */);
}
- private void startAssistInternal(Bundle args) {
- if (mAssistComponent != null) {
- if (isAssistantService()) {
- startVoiceInteractor(args);
- } else {
- startAssistActivity(args);
- }
+ private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent,
+ boolean isService) {
+ if (isService) {
+ startVoiceInteractor(args);
+ } else {
+ startAssistActivity(args, assistComponent);
}
}
- private void startAssistActivity(Bundle args) {
+ private void startAssistActivity(Bundle args, @NonNull ComponentName assistComponent) {
if (!mBar.isDeviceProvisioned()) {
return;
}
@@ -193,9 +173,7 @@
if (intent == null) {
return;
}
- if (mAssistComponent != null) {
- intent.setComponent(mAssistComponent);
- }
+ intent.setComponent(assistComponent);
intent.putExtras(args);
if (structureEnabled) {
@@ -243,13 +221,9 @@
mWindowManager.removeViewImmediate(mView);
}
- private void maybeSwapSearchIcon() {
- if (mAssistComponent != null) {
- replaceDrawable(mView.getOrb().getLogo(), mAssistComponent, ASSIST_ICON_METADATA_NAME,
- isAssistantService());
- } else {
- mView.getOrb().getLogo().setImageDrawable(null);
- }
+ private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
+ replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
+ isService);
}
public void replaceDrawable(ImageView v, ComponentName component, String name,
@@ -283,28 +257,15 @@
v.setImageDrawable(null);
}
- private boolean isAssistantService() {
- return mAssistComponent == null ?
- false : mAssistComponent.equals(getVoiceInteractorComponentName());
- }
-
- private void updateAssistInfo() {
- mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.USER_CURRENT);
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("AssistManager state:");
- pw.print(" mAssistComponent="); pw.println(mAssistComponent);
+ @Nullable
+ private ComponentName getAssistInfo() {
+ return mAssistUtils.getAssistComponentForUser(UserHandle.USER_CURRENT);
}
public void showDisclosure() {
mAssistDisclosure.postShow();
}
- public void onUserSwitched(int newUserId) {
- updateAssistInfo();
- }
-
public void onLockscreenShown() {
mAssistUtils.onLockscreenShown();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
new file mode 100644
index 0000000..86bea87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.view.MotionEvent;
+
+import java.util.HashMap;
+
+/**
+ * A classifier which looks at the speed and distance between successive points of a Stroke.
+ * It looks at two consecutive speeds between two points and calculates the ratio between them.
+ * The final result is the maximum of these values. It does the same for distances. If some speed
+ * or distance is equal to zero then the ratio between this and the next part is not calculated. To
+ * the duration of each part there is added one nanosecond so that it is always possible to
+ * calculate the speed of a part.
+ */
+public class AccelerationClassifier extends StrokeClassifier {
+ private final HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
+
+ public AccelerationClassifier(ClassifierData classifierData) {
+ mClassifierData = classifierData;
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mStrokeMap.clear();
+ }
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+ Point point = stroke.getPoints().get(stroke.getPoints().size() - 1);
+ if (mStrokeMap.get(stroke) == null) {
+ mStrokeMap.put(stroke, new Data(point));
+ } else {
+ mStrokeMap.get(stroke).addPoint(point);
+ }
+ }
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ Data data = mStrokeMap.get(stroke);
+ return SpeedRatioEvaluator.evaluate(data.maxSpeedRatio)
+ + DistanceRatioEvaluator.evaluate(data.maxDistanceRatio);
+ }
+
+ private static class Data {
+ public Point previousPoint;
+ public float previousSpeed;
+ public float previousDistance;
+ public float maxSpeedRatio;
+ public float maxDistanceRatio;
+
+ public Data(Point point) {
+ previousPoint = point;
+ previousSpeed = previousDistance = 0.0f;
+ maxDistanceRatio = maxSpeedRatio = 0.0f;
+ }
+
+ public void addPoint(Point point) {
+ float distance = previousPoint.dist(point);
+ float duration = (float) (point.timeOffsetNano - previousPoint.timeOffsetNano + 1);
+ float speed = distance / duration;
+ if (previousDistance != 0.0f) {
+ maxDistanceRatio = Math.max(maxDistanceRatio, distance / previousDistance);
+ }
+
+ if (previousSpeed != 0.0f) {
+ maxSpeedRatio = Math.max(maxSpeedRatio, speed / previousSpeed);
+ }
+
+ previousDistance = distance;
+ previousSpeed = speed;
+ previousPoint = point;
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
new file mode 100644
index 0000000..a6ebc0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.view.MotionEvent;
+
+import java.lang.Math;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A classifier which calculates the variance of differences between successive angles in a stroke.
+ * For each stroke it keeps its last three points. If some successive points are the same, it
+ * ignores the repetitions. If a new point is added, the classifier calculates the angle between
+ * the last three points. After that, it calculates the difference between this angle and the
+ * previously calculated angle. Then it calculates the variance of the differences from a stroke.
+ * To the differences there is artificially added value 0.0 and the difference between the first
+ * angle and PI (angles are in radians). It helps with strokes which have few points and punishes
+ * more strokes which are not smooth.
+ *
+ * This classifier also tries to split the stroke into two parts in the place in which the biggest
+ * angle is. It calculates the angle variance of the two parts and sums them up. The reason the
+ * classifier is doing this, is because some human swipes at the beginning go for a moment in one
+ * direction and then they rapidly change direction for the rest of the stroke (like a tick). The
+ * final result is the minimum of angle variance of the whole stroke and the sum of angle variances
+ * of the two parts split up. The classifier tries the tick option only if the first part is
+ * shorter than the second part.
+ *
+ * Additionally, the classifier classifies the angles as left angles (those angles which value is
+ * in [0.0, PI - ANGLE_DEVIATION) interval), straight angles
+ * ([PI - ANGLE_DEVIATION, PI + ANGLE_DEVIATION] interval) and right angles
+ * ((PI + ANGLE_DEVIATION, 2 * PI) interval) and then calculates the percentage of angles which are
+ * in the same direction (straight angles can be left angels or right angles)
+ */
+public class AnglesClassifier extends StrokeClassifier {
+ private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
+
+ public AnglesClassifier(ClassifierData classifierData) {
+ mClassifierData = classifierData;
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mStrokeMap.clear();
+ }
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+
+ if (mStrokeMap.get(stroke) == null) {
+ mStrokeMap.put(stroke, new Data());
+ }
+ mStrokeMap.get(stroke).addPoint(stroke.getPoints().get(stroke.getPoints().size() - 1));
+ }
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ Data data = mStrokeMap.get(stroke);
+ return AnglesVarianceEvaluator.evaluate(data.getAnglesVariance())
+ + AnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
+ }
+
+ private static class Data {
+ private final float ANGLE_DEVIATION = (float) Math.PI / 20.0f;
+
+ private List<Point> mLastThreePoints = new ArrayList<>();
+ private float mFirstAngleVariance;
+ private float mPreviousAngle;
+ private float mBiggestAngle;
+ private float mSumSquares;
+ private float mSecondSumSquares;
+ private float mSum;
+ private float mSecondSum;
+ private float mCount;
+ private float mSecondCount;
+ private float mFirstLength;
+ private float mLength;
+ private float mAnglesCount;
+ private float mLeftAngles;
+ private float mRightAngles;
+ private float mStraightAngles;
+
+ public Data() {
+ mFirstAngleVariance = 0.0f;
+ mPreviousAngle = (float) Math.PI;
+ mBiggestAngle = 0.0f;
+ mSumSquares = mSecondSumSquares = 0.0f;
+ mSum = mSecondSum = 0.0f;
+ mCount = mSecondCount = 1.0f;
+ mLength = mFirstLength = 0.0f;
+ mAnglesCount = mLeftAngles = mRightAngles = mStraightAngles = 0.0f;
+ }
+
+ public void addPoint(Point point) {
+ // Checking if the added point is different than the previously added point
+ // Repetitions are being ignored so that proper angles are calculated.
+ if (mLastThreePoints.isEmpty()
+ || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(point)) {
+ if (!mLastThreePoints.isEmpty()) {
+ mLength += mLastThreePoints.get(mLastThreePoints.size() - 1).dist(point);
+ }
+ mLastThreePoints.add(point);
+ if (mLastThreePoints.size() == 4) {
+ mLastThreePoints.remove(0);
+
+ float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
+ mLastThreePoints.get(2));
+
+ mAnglesCount++;
+ if (angle < Math.PI - ANGLE_DEVIATION) {
+ mLeftAngles++;
+ } else if (angle <= Math.PI + ANGLE_DEVIATION) {
+ mStraightAngles++;
+ } else {
+ mRightAngles++;
+ }
+
+ float difference = angle - mPreviousAngle;
+
+ // If this is the biggest angle of the stroke so then we save the value of
+ // the angle variance so far and start to count the values for the angle
+ // variance of the second part.
+ if (mBiggestAngle < angle) {
+ mBiggestAngle = angle;
+ mFirstLength = mLength;
+ mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount);
+ mSecondSumSquares = 0.0f;
+ mSecondSum = 0.0f;
+ mSecondCount = 1.0f;
+ } else {
+ mSecondSum += difference;
+ mSecondSumSquares += difference * difference;
+ mSecondCount += 1.0;
+ }
+
+ mSum += difference;
+ mSumSquares += difference * difference;
+ mCount += 1.0;
+ mPreviousAngle = angle;
+ }
+ }
+ }
+
+ public float getAnglesVariance(float sumSquares, float sum, float count) {
+ return sumSquares / count - (sum / count) * (sum / count);
+ }
+
+ public float getAnglesVariance() {
+ float anglesVariance = getAnglesVariance(mSumSquares, mSum, mCount);
+ if (mFirstLength < mLength / 2f) {
+ anglesVariance = Math.min(anglesVariance, mFirstAngleVariance
+ + getAnglesVariance(mSecondSumSquares, mSecondSum, mSecondCount));
+ }
+ return anglesVariance;
+ }
+
+ public float getAnglesPercentage() {
+ if (mAnglesCount == 0.0f) {
+ return 1.0f;
+ }
+ return (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount;
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
new file mode 100644
index 0000000..a0ceb29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class AnglesPercentageEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 1.00) evaluation++;
+ if (value < 0.95) evaluation++;
+ if (value < 0.90) evaluation++;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
new file mode 100644
index 0000000..99cc1a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class AnglesVarianceEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value > 0.05) evaluation++;
+ if (value > 0.10) evaluation++;
+ if (value > 0.20) evaluation++;
+ if (value > 0.40) evaluation++;
+ if (value > 0.80) evaluation++;
+ if (value > 1.50) evaluation++;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
new file mode 100644
index 0000000..89d20de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.hardware.SensorEvent;
+import android.view.MotionEvent;
+
+/**
+ * An abstract class for classifiers for touch and sensor events.
+ */
+public abstract class Classifier {
+ public static final int QUICK_SETTINGS = 0;
+ public static final int NOTIFICATION_DISMISS = 1;
+ public static final int NOTIFICATION_DRAG_DOWN = 2;
+ public static final int NOTIFICATION_DOUBLE_TAP = 3;
+ public static final int UNLOCK = 4;
+ public static final int LEFT_AFFORDANCE = 5;
+ public static final int RIGHT_AFFORDANCE = 6;
+ public static final int GENERIC = 7;
+
+ /**
+ * Contains all the information about touch events from which the classifier can query
+ */
+ protected ClassifierData mClassifierData;
+
+ /**
+ * Informs the classifier that a new touch event has occurred
+ */
+ public void onTouchEvent(MotionEvent event) {
+ }
+
+ /**
+ * Informs the classifier that a sensor change occurred
+ */
+ public void onSensorChanged(SensorEvent event) {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
new file mode 100644
index 0000000..c83c74f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.util.SparseArray;
+import android.view.MotionEvent;
+
+import java.util.ArrayList;
+
+/**
+ * Contains data which is used to classify interaction sequences on the lockscreen. It does, for
+ * example, provide information on the current touch state.
+ */
+public class ClassifierData {
+ private SparseArray<Stroke> mCurrentStrokes = new SparseArray<>();
+ private ArrayList<Stroke> mEndingStrokes = new ArrayList<>();
+ private final float mDpi;
+
+ public ClassifierData(float dpi) {
+ mDpi = dpi;
+ }
+
+ public void update(MotionEvent event) {
+ mEndingStrokes.clear();
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mCurrentStrokes.clear();
+ }
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ int id = event.getPointerId(i);
+ if (mCurrentStrokes.get(id) == null) {
+ mCurrentStrokes.put(id, new Stroke(event.getEventTimeNano(), mDpi));
+ }
+ mCurrentStrokes.get(id).addPoint(event.getX(i), event.getY(i),
+ event.getEventTimeNano());
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
+ || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+ mEndingStrokes.add(getStroke(id));
+ }
+ }
+ }
+
+ public void cleanUp(MotionEvent event) {
+ mEndingStrokes.clear();
+ int action = event.getActionMasked();
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ int id = event.getPointerId(i);
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
+ || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+ mCurrentStrokes.remove(id);
+ }
+ }
+ }
+
+ /**
+ * @return the list of Strokes which are ending in the recently added MotionEvent
+ */
+ public ArrayList<Stroke> getEndingStrokes() {
+ return mEndingStrokes;
+ }
+
+ /**
+ * @param id the id from MotionEvent
+ * @return the Stroke assigned to the id
+ */
+ public Stroke getStroke(int id) {
+ return mCurrentStrokes.get(id);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
new file mode 100644
index 0000000..299d0e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the general direction of a stroke and evaluates it depending on
+ * the type of action that takes place.
+ */
+public class DirectionClassifier extends StrokeClassifier {
+ public DirectionClassifier(ClassifierData classifierData) {
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ Point firstPoint = stroke.getPoints().get(0);
+ Point lastPoint = stroke.getPoints().get(stroke.getPoints().size() - 1);
+ return DirectionEvaluator.evaluate(lastPoint.x - firstPoint.x, lastPoint.y - firstPoint.y,
+ type);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java
new file mode 100644
index 0000000..e20b1ca6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class DirectionEvaluator {
+ public static float evaluate(float xDiff, float yDiff, int type) {
+ float falsingEvaluation = 5.5f;
+ boolean vertical = Math.abs(yDiff) >= Math.abs(xDiff);
+ switch (type) {
+ case Classifier.QUICK_SETTINGS:
+ case Classifier.NOTIFICATION_DRAG_DOWN:
+ if (!vertical || yDiff <= 0.0) {
+ return falsingEvaluation;
+ }
+ break;
+ case Classifier.NOTIFICATION_DISMISS:
+ if (vertical) {
+ return falsingEvaluation;
+ }
+ break;
+ case Classifier.UNLOCK:
+ if (!vertical || yDiff >= 0.0) {
+ return falsingEvaluation;
+ }
+ break;
+ case Classifier.LEFT_AFFORDANCE:
+ if (xDiff < 0.0 && yDiff > 0.0) {
+ return falsingEvaluation;
+ }
+ break;
+ case Classifier.RIGHT_AFFORDANCE:
+ if (xDiff > 0.0 && yDiff > 0.0) {
+ return falsingEvaluation;
+ }
+ default:
+ break;
+ }
+ return 0.0f;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java
new file mode 100644
index 0000000..8acb009
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class DistanceRatioEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value <= 1.0) evaluation++;
+ if (value <= 0.5) evaluation++;
+ if (value > 4.0) evaluation++;
+ if (value > 7.0) evaluation++;
+ if (value > 14.0) evaluation++;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
new file mode 100644
index 0000000..8924694
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the ratio between the duration of the stroke and its number of
+ * points.
+ */
+public class DurationCountClassifier extends StrokeClassifier {
+ public DurationCountClassifier(ClassifierData classifierData) {
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ return DurationCountEvaluator.evaluate(stroke.getDurationSeconds() / stroke.getCount());
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java
new file mode 100644
index 0000000..5395983
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+
+public class DurationCountEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 0.0105) evaluation++;
+ if (value < 0.00909) evaluation++;
+ if (value < 0.00667) evaluation++;
+ if (value > 0.0333) evaluation++;
+ if (value > 0.0500) evaluation++;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
new file mode 100644
index 0000000..78bc0dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the distance between the first and the last point from the stroke.
+ */
+public class EndPointLengthClassifier extends StrokeClassifier {
+ public EndPointLengthClassifier(ClassifierData classifierData) {
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ return EndPointLengthEvaluator.evaluate(stroke.getEndPointLength());
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java
new file mode 100644
index 0000000..bb2f1c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class EndPointLengthEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 0.05) evaluation += 2.0;
+ if (value < 0.1) evaluation += 2.0;
+ if (value < 0.2) evaluation += 2.0;
+ if (value < 0.3) evaluation += 2.0;
+ if (value < 0.4) evaluation += 2.0;
+ if (value < 0.5) evaluation += 2.0;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
new file mode 100644
index 0000000..c125e00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the ratio between the total length covered by the stroke and the
+ * distance between the first and last point from this stroke.
+ */
+public class EndPointRatioClassifier extends StrokeClassifier {
+ public EndPointRatioClassifier(ClassifierData classifierData) {
+ mClassifierData = classifierData;
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ if (stroke.getTotalLength() == 0.0f) {
+ return 1.0f;
+ }
+ return EndPointRatioEvaluator.evaluate(
+ stroke.getEndPointLength() / stroke.getTotalLength());
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java
new file mode 100644
index 0000000..529fcec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class EndPointRatioEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 0.85) evaluation++;
+ if (value < 0.75) evaluation++;
+ if (value < 0.65) evaluation++;
+ if (value < 0.55) evaluation++;
+ if (value < 0.45) evaluation++;
+ if (value < 0.35) evaluation++;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
new file mode 100644
index 0000000..735a7c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.MotionEvent;
+
+import com.android.systemui.analytics.DataCollector;
+import com.android.systemui.statusbar.StatusBarState;
+
+/**
+ * When the phone is locked, listens to touch, sensor and phone events and sends them to
+ * DataCollector and HumanInteractionClassifier.
+ *
+ * It does not collect touch events when the bouncer shows up.
+ */
+public class FalsingManager implements SensorEventListener {
+ private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
+
+ private static final int[] CLASSIFIER_SENSORS = new int[] {
+ Sensor.TYPE_PROXIMITY,
+ };
+
+ private static final int[] COLLECTOR_SENSORS = new int[] {
+ Sensor.TYPE_ACCELEROMETER,
+ Sensor.TYPE_GYROSCOPE,
+ Sensor.TYPE_PROXIMITY,
+ Sensor.TYPE_LIGHT,
+ Sensor.TYPE_ROTATION_VECTOR,
+ };
+
+ private final Handler mHandler = new Handler();
+ private final Context mContext;
+
+ private final SensorManager mSensorManager;
+ private final DataCollector mDataCollector;
+ private final HumanInteractionClassifier mHumanInteractionClassifier;
+
+ private static FalsingManager sInstance = null;
+
+ private boolean mEnforceBouncer = false;
+ private boolean mBouncerOn = false;
+ private boolean mSessionActive = false;
+ private int mState = StatusBarState.SHADE;
+
+ protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateConfiguration();
+ }
+ };
+
+ private FalsingManager(Context context) {
+ mContext = context;
+ mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+ mDataCollector = DataCollector.getInstance(mContext);
+ mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
+ mSettingsObserver,
+ UserHandle.USER_ALL);
+
+ updateConfiguration();
+ }
+
+ public static FalsingManager getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new FalsingManager(context);
+ }
+ return sInstance;
+ }
+
+ private void updateConfiguration() {
+ mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
+ ENFORCE_BOUNCER, 0);
+ }
+
+ private boolean sessionEntrypoint() {
+ if (!mSessionActive && isEnabled() &&
+ (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
+ onSessionStart();
+ return true;
+ }
+ return false;
+ }
+
+ private void sessionExitpoint() {
+ if (mSessionActive) {
+ mSessionActive = false;
+ mSensorManager.unregisterListener(this);
+ }
+ }
+
+ private void onSessionStart() {
+ mBouncerOn = false;
+ mSessionActive = true;
+
+ if (mHumanInteractionClassifier.isEnabled()) {
+ registerSensors(CLASSIFIER_SENSORS);
+ }
+ if (mDataCollector.isEnabled()) {
+ registerSensors(COLLECTOR_SENSORS);
+ }
+ }
+
+ private void registerSensors(int [] sensors) {
+ for (int sensorType : sensors) {
+ Sensor s = mSensorManager.getDefaultSensor(sensorType);
+ if (s != null) {
+ mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
+ }
+ }
+ }
+
+ public boolean isClassiferEnabled() {
+ return mHumanInteractionClassifier.isEnabled();
+ }
+
+ private boolean isEnabled() {
+ return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
+ }
+
+ /**
+ * @return true if the classifier determined that this is not a human interacting with the phone
+ */
+ public boolean isFalseTouch() {
+ return mHumanInteractionClassifier.isFalseTouch();
+ }
+
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ mDataCollector.onSensorChanged(event);
+ mHumanInteractionClassifier.onSensorChanged(event);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ mDataCollector.onAccuracyChanged(sensor, accuracy);
+ }
+
+ public boolean shouldEnforceBouncer() {
+ return mEnforceBouncer;
+ }
+
+ public void setStatusBarState(int state) {
+ mState = state;
+ }
+
+ public void onScreenTurningOn() {
+ if (sessionEntrypoint()) {
+ mDataCollector.onScreenTurningOn();
+ }
+ }
+
+ public void onScreenOnFromTouch() {
+ if (sessionEntrypoint()) {
+ mDataCollector.onScreenOnFromTouch();
+ }
+ }
+
+ public void onScreenOff() {
+ mDataCollector.onScreenOff();
+ sessionExitpoint();
+ }
+
+ public void onSucccessfulUnlock() {
+ mDataCollector.onSucccessfulUnlock();
+ sessionExitpoint();
+ }
+
+ public void onBouncerShown() {
+ if (!mBouncerOn) {
+ mBouncerOn = true;
+ mDataCollector.onBouncerShown();
+ }
+ }
+
+ public void onBouncerHidden() {
+ if (mBouncerOn) {
+ mBouncerOn = false;
+ mDataCollector.onBouncerHidden();
+ }
+ }
+
+ public void onQsDown() {
+ mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
+ mDataCollector.onQsDown();
+ }
+
+ public void setQsExpanded(boolean expanded) {
+ mDataCollector.setQsExpanded(expanded);
+ }
+
+ public void onTrackingStarted() {
+ mHumanInteractionClassifier.setType(Classifier.UNLOCK);
+ mDataCollector.onTrackingStarted();
+ }
+
+ public void onTrackingStopped() {
+ mDataCollector.onTrackingStopped();
+ }
+
+ public void onNotificationActive() {
+ mDataCollector.onNotificationActive();
+ }
+
+ public void onNotificationDoubleTap() {
+ mDataCollector.onNotificationDoubleTap();
+ }
+
+ public void setNotificationExpanded() {
+ mDataCollector.setNotificationExpanded();
+ }
+
+ public void onNotificatonStartDraggingDown() {
+ mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
+ mDataCollector.onNotificatonStartDraggingDown();
+ }
+
+ public void onNotificatonStopDraggingDown() {
+ mDataCollector.onNotificatonStopDraggingDown();
+ }
+
+ public void onNotificationDismissed() {
+ mDataCollector.onNotificationDismissed();
+ }
+
+ public void onNotificatonStartDismissing() {
+ mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
+ mDataCollector.onNotificatonStartDismissing();
+ }
+
+ public void onNotificatonStopDismissing() {
+ mDataCollector.onNotificatonStopDismissing();
+ }
+
+ public void onCameraOn() {
+ mDataCollector.onCameraOn();
+ }
+
+ public void onLeftAffordanceOn() {
+ mDataCollector.onLeftAffordanceOn();
+ }
+
+ public void onAffordanceSwipingStarted(boolean rightCorner) {
+ if (rightCorner) {
+ mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
+ } else {
+ mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE);
+ }
+ mDataCollector.onAffordanceSwipingStarted(rightCorner);
+ }
+
+ public void onAffordanceSwipingAborted() {
+ mDataCollector.onAffordanceSwipingAborted();
+ }
+
+ public void onUnlockHintStarted() {
+ mDataCollector.onUnlockHintStarted();
+ }
+
+ public void onCameraHintStarted() {
+ mDataCollector.onCameraHintStarted();
+ }
+
+ public void onLeftAffordanceHintStarted() {
+ mDataCollector.onLeftAffordanceHintStarted();
+ }
+
+ public void onTouchEvent(MotionEvent event, int width, int height) {
+ if (mSessionActive && !mBouncerOn) {
+ mDataCollector.onTouchEvent(event, width, height);
+ mHumanInteractionClassifier.onTouchEvent(event);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java
new file mode 100644
index 0000000..11388fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * An abstract class for classifiers which classify the whole gesture (all the strokes which
+ * occurred from DOWN event to UP/CANCEL event)
+ */
+public abstract class GestureClassifier extends Classifier {
+
+ /**
+ * @param type the type of action for which this method is called
+ * @return a non-negative value which is used to determine whether the most recent gesture is a
+ * false interaction; the bigger the value the greater the chance that this a false
+ * interaction.
+ */
+ public abstract float getFalseTouchEvaluation(int type);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
new file mode 100644
index 0000000..85a9bee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import java.util.ArrayList;
+
+/**
+ * Holds the evaluations for ended strokes and gestures. These values are decreased through time.
+ */
+public class HistoryEvaluator {
+ private static final float INTERVAL = 50.0f;
+ private static final float HISTORY_FACTOR = 0.9f;
+ private static final float EPSILON = 1e-5f;
+
+ private final ArrayList<Data> mStrokes = new ArrayList<>();
+ private final ArrayList<Data> mGestureWeights = new ArrayList<>();
+ private long mLastUpdate;
+
+ public HistoryEvaluator() {
+ mLastUpdate = System.currentTimeMillis();
+ }
+
+ public void addStroke(float evaluation) {
+ decayValue();
+ mStrokes.add(new Data(evaluation));
+ }
+
+ public void addGesture(float evaluation) {
+ decayValue();
+ mGestureWeights.add(new Data(evaluation));
+ }
+
+ /**
+ * Calculates the weighted average of strokes and adds to it the weighted average of gestures
+ */
+ public float getEvaluation() {
+ return weightedAverage(mStrokes) + weightedAverage(mGestureWeights);
+ }
+
+ private float weightedAverage(ArrayList<Data> list) {
+ float sumValue = 0.0f;
+ float sumWeight = 0.0f;
+ int size = list.size();
+ for (int i = 0; i < size; i++) {
+ Data data = list.get(i);
+ sumValue += data.evaluation * data.weight;
+ sumWeight += data.weight;
+ }
+
+ if (sumWeight == 0.0f) {
+ return 0.0f;
+ }
+
+ return sumValue / sumWeight;
+ }
+
+ private void decayValue() {
+ long currentTimeMillis = System.currentTimeMillis();
+
+ // All weights are multiplied by HISTORY_FACTOR after each INTERVAL milliseconds.
+ float factor = (float) Math.pow(HISTORY_FACTOR,
+ (float) (currentTimeMillis - mLastUpdate) / INTERVAL);
+
+ decayValue(mStrokes, factor);
+ decayValue(mGestureWeights, factor);
+ mLastUpdate = currentTimeMillis;
+ }
+
+ private void decayValue(ArrayList<Data> list, float factor) {
+ int size = list.size();
+ for (int i = 0; i < size; i++) {
+ list.get(i).weight *= factor;
+ }
+
+ // Removing evaluations with such small weights that they do not matter anymore
+ while (!list.isEmpty() && isZero(list.get(0).weight)) {
+ list.remove(0);
+ }
+ }
+
+ private boolean isZero(float x) {
+ return x <= EPSILON && x >= -EPSILON;
+ }
+
+ /**
+ * For each stroke it holds its initial value and the current weight. Initially the
+ * weight is set to 1.0
+ */
+ private static class Data {
+ public float evaluation;
+ public float weight;
+
+ public Data(float evaluation) {
+ this.evaluation = evaluation;
+ weight = 1.0f;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
new file mode 100644
index 0000000..a7a5694
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.SensorEvent;
+import android.os.Build;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+/**
+ * An classifier trying to determine whether it is a human interacting with the phone or not.
+ */
+public class HumanInteractionClassifier extends Classifier {
+ private static final String HIC_ENABLE = "HIC_enable";
+ private static final float FINGER_DISTANCE = 0.1f;
+ private static HumanInteractionClassifier sInstance = null;
+
+ private final Handler mHandler = new Handler();
+ private final Context mContext;
+
+ private ArrayList<StrokeClassifier> mStrokeClassifiers = new ArrayList<>();
+ private ArrayList<GestureClassifier> mGestureClassifiers = new ArrayList<>();
+ private ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>();
+ private final int mStrokeClassifiersSize;
+ private final int mGestureClassifiersSize;
+ private final float mDpi;
+
+ private HistoryEvaluator mHistoryEvaluator;
+ private boolean mEnableClassifier = true;
+ private int mCurrentType = Classifier.GENERIC;
+
+ protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateConfiguration();
+ }
+ };
+
+ private HumanInteractionClassifier(Context context) {
+ mContext = context;
+ DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+
+ // If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
+ // were to be used separately. Due negligible differences in xdpi and ydpi we can just
+ // take the average.
+ mDpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
+ mClassifierData = new ClassifierData(mDpi);
+ mHistoryEvaluator = new HistoryEvaluator();
+
+ mStrokeClassifiers.add(new AnglesClassifier(mClassifierData));
+ mStrokeClassifiers.add(new SpeedClassifier(mClassifierData));
+ mStrokeClassifiers.add(new DurationCountClassifier(mClassifierData));
+ mStrokeClassifiers.add(new EndPointRatioClassifier(mClassifierData));
+ mStrokeClassifiers.add(new EndPointLengthClassifier(mClassifierData));
+ mStrokeClassifiers.add(new AccelerationClassifier(mClassifierData));
+ mStrokeClassifiers.add(new SpeedAnglesClassifier(mClassifierData));
+ mStrokeClassifiers.add(new LengthCountClassifier(mClassifierData));
+ mStrokeClassifiers.add(new DirectionClassifier(mClassifierData));
+
+ mGestureClassifiers.add(new PointerCountClassifier(mClassifierData));
+ mGestureClassifiers.add(new ProximityClassifier(mClassifierData));
+
+ mStrokeClassifiersSize = mStrokeClassifiers.size();
+ mGestureClassifiersSize = mGestureClassifiers.size();
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(HIC_ENABLE), false,
+ mSettingsObserver,
+ UserHandle.USER_ALL);
+
+ updateConfiguration();
+ }
+
+ public static HumanInteractionClassifier getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new HumanInteractionClassifier(context);
+ }
+ return sInstance;
+ }
+
+ private void updateConfiguration() {
+ mEnableClassifier = Build.IS_DEBUGGABLE && 0 != Settings.Global.getInt(
+ mContext.getContentResolver(),
+ HIC_ENABLE, 0);
+ }
+
+ public void setType(int type) {
+ mCurrentType = type;
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ if (!mEnableClassifier) {
+ return;
+ }
+
+ // If the user is dragging down the notification, he might want to drag it down
+ // enough to see the content, read it for a while and then lift the finger to open
+ // the notification. This kind of motion scores very bad in the Classifier so the
+ // MotionEvents which are close to the current position of the finger are not
+ // sent to the classifiers until the finger moves far enough. When the finger if lifted
+ // up, the last MotionEvent which was far enough from the finger is set as the final
+ // MotionEvent and sent to the Classifiers.
+ if (mCurrentType == Classifier.NOTIFICATION_DRAG_DOWN) {
+ mBufferedEvents.add(MotionEvent.obtain(event));
+ Point pointEnd = new Point(event.getX() / mDpi, event.getY() / mDpi);
+
+ while (pointEnd.dist(new Point(mBufferedEvents.getFirst().getX() / mDpi,
+ mBufferedEvents.getFirst().getY() / mDpi)) > FINGER_DISTANCE) {
+ addTouchEvent(mBufferedEvents.getFirst());
+ mBufferedEvents.remove();
+ }
+
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_UP) {
+ mBufferedEvents.getFirst().setAction(MotionEvent.ACTION_UP);
+ addTouchEvent(mBufferedEvents.getFirst());
+ mBufferedEvents.clear();
+ }
+ } else {
+ addTouchEvent(event);
+ }
+ }
+
+ private void addTouchEvent(MotionEvent event) {
+ mClassifierData.update(event);
+
+ for (int i = 0; i < mStrokeClassifiersSize; i++) {
+ mStrokeClassifiers.get(i).onTouchEvent(event);
+ }
+
+ for (int i = 0; i < mGestureClassifiersSize; i++) {
+ mGestureClassifiers.get(i).onTouchEvent(event);
+ }
+
+ int size = mClassifierData.getEndingStrokes().size();
+ for (int i = 0; i < size; i++) {
+ Stroke stroke = mClassifierData.getEndingStrokes().get(i);
+ float evaluation = 0.0f;
+ for (int j = 0; j < mStrokeClassifiersSize; j++) {
+ evaluation += mStrokeClassifiers.get(j).getFalseTouchEvaluation(
+ mCurrentType, stroke);
+ }
+ mHistoryEvaluator.addStroke(evaluation);
+ }
+
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ float evaluation = 0.0f;
+ for (int i = 0; i < mGestureClassifiersSize; i++) {
+ evaluation += mGestureClassifiers.get(i).getFalseTouchEvaluation(mCurrentType);
+ }
+ mHistoryEvaluator.addGesture(evaluation);
+ setType(Classifier.GENERIC);
+ }
+
+ mClassifierData.cleanUp(event);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ for (int i = 0; i < mStrokeClassifiers.size(); i++) {
+ mStrokeClassifiers.get(i).onSensorChanged(event);
+ }
+
+ for (int i = 0; i < mGestureClassifiers.size(); i++) {
+ mGestureClassifiers.get(i).onSensorChanged(event);
+ }
+ }
+
+ public boolean isFalseTouch() {
+ if (mEnableClassifier) {
+ return mHistoryEvaluator.getEvaluation() >= 5.0f;
+ }
+ return false;
+ }
+
+ public boolean isEnabled() {
+ return mEnableClassifier;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
new file mode 100644
index 0000000..cedf467
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the ratio between the length of the stroke and its number of
+ * points. The number of points is subtracted by 2 because the UP event comes in with some delay
+ * and it should not influence the ratio and also strokes which are long and have a small number
+ * of points are punished more (these kind of strokes are usually bad ones and they tend to score
+ * well in other classifiers).
+ */
+public class LengthCountClassifier extends StrokeClassifier {
+ public LengthCountClassifier(ClassifierData classifierData) {
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ return LengthCountEvaluator.evaluate(stroke.getTotalLength()
+ / Math.max(1.0f, stroke.getCount() - 2));
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
new file mode 100644
index 0000000..dac7a6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the ratio between the length of the stroke and its number of
+ * points.
+ */
+public class LengthCountEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 0.09) evaluation++;
+ if (value < 0.05) evaluation++;
+ if (value < 0.02) evaluation++;
+ if (value > 0.6) evaluation++;
+ if (value > 0.9) evaluation++;
+ if (value > 1.2) evaluation++;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Point.java b/packages/SystemUI/src/com/android/systemui/classifier/Point.java
new file mode 100644
index 0000000..f3dc2be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Point.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class Point {
+ public float x;
+ public float y;
+ public long timeOffsetNano;
+
+ public Point(float x, float y) {
+ this.x = x;
+ this.y = y;
+ this.timeOffsetNano = 0;
+ }
+
+ public Point(float x, float y, long timeOffsetNano) {
+ this.x = x;
+ this.y = y;
+ this.timeOffsetNano = timeOffsetNano;
+ }
+
+ public boolean equals(Point p) {
+ return x == p.x && y == p.y;
+ }
+
+ public float dist(Point a) {
+ return (float) Math.hypot(a.x - x, a.y - y);
+ }
+
+ /**
+ * Calculates the cross product of vec(this, a) and vec(this, b) where vec(x,y) is the
+ * vector from point x to point y
+ */
+ public float crossProduct(Point a, Point b) {
+ return (a.x - x) * (b.y - y) - (a.y - y) * (b.x - x);
+ }
+
+ /**
+ * Calculates the dot product of vec(this, a) and vec(this, b) where vec(x,y) is the
+ * vector from point x to point y
+ */
+ public float dotProduct(Point a, Point b) {
+ return (a.x - x) * (b.x - x) + (a.y - y) * (b.y - y);
+ }
+
+ /**
+ * Calculates the angle in radians created by points (a, this, b). If any two of these points
+ * are the same, the method will return 0.0f
+ *
+ * @return the angle in radians
+ */
+ public float getAngle(Point a, Point b) {
+ float dist1 = dist(a);
+ float dist2 = dist(b);
+
+ if (dist1 == 0.0f || dist2 == 0.0f) {
+ return 0.0f;
+ }
+
+ float crossProduct = crossProduct(a, b);
+ float dotProduct = dotProduct(a, b);
+ float cos = Math.min(1.0f, Math.max(-1.0f, dotProduct / dist1 / dist2));
+ float angle = (float) Math.acos(cos);
+ if (crossProduct < 0.0) {
+ angle = 2.0f * (float) Math.PI - angle;
+ }
+ return angle;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
new file mode 100644
index 0000000..5097b63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.view.MotionEvent;
+
+/**
+ * A classifier which looks at the total number of traces in the whole gesture.
+ */
+public class PointerCountClassifier extends GestureClassifier {
+ private int mCount;
+
+ public PointerCountClassifier(ClassifierData classifierData) {
+ mCount = 0;
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mCount = 1;
+ }
+
+ if (action == MotionEvent.ACTION_POINTER_DOWN) {
+ ++mCount;
+ }
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type) {
+ return PointerCountEvaluator.evaluate(mCount);
+ }
+}
diff --git a/tools/aidl/os.h b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
similarity index 60%
copy from tools/aidl/os.h
copy to packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
index 752ed47..d7a3af0 100644
--- a/tools/aidl/os.h
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
@@ -1,26 +1,23 @@
/*
- * Copyright 2015, The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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.
+ * limitations under the License
*/
-#ifndef AIDL_OS_H_
-#define AIDL_OS_H_
+package com.android.systemui.classifier;
-#if defined(_WIN32)
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
-
-#endif // AIDL_OS_H_
+public class PointerCountEvaluator {
+ public static float evaluate(int value) {
+ return (value - 1) * (value - 1);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
new file mode 100644
index 0000000..6995064
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.view.MotionEvent;
+
+/**
+ * A classifier which looks at the proximity sensor during the gesture. It calculates the percentage
+ * the proximity sensor showing the near state during the whole gesture
+ */
+public class ProximityClassifier extends GestureClassifier {
+ private long mGestureStartTimeNano;
+ private long mNearStartTimeNano;
+ private long mNearDuration;
+ private boolean mNear;
+ private float mAverageNear;
+
+ public ProximityClassifier(ClassifierData classifierData) {
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
+ update(event.values[0] < event.sensor.getMaximumRange(), event.timestamp);
+ }
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mGestureStartTimeNano = event.getEventTimeNano();
+ mNearStartTimeNano = event.getEventTimeNano();
+ mNearDuration = 0;
+ }
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ update(mNear, event.getEventTimeNano());
+ long duration = event.getEventTimeNano() - mGestureStartTimeNano;
+
+ if (duration == 0) {
+ mAverageNear = mNear ? 1.0f : 0.0f;
+ } else {
+ mAverageNear = (float) mNearDuration / (float) duration;
+ }
+ }
+ }
+
+
+ /**
+ * @param near is the sensor showing the near state right now
+ * @param timestampNano time of this event in nanoseconds
+ */
+ private void update(boolean near, long timestampNano) {
+ // This if is necessary because MotionEvents and SensorEvents do not come in
+ // chronological order
+ if (timestampNano > mNearStartTimeNano) {
+ // if the state before was near then add the difference of the current time and
+ // mNearStartTimeNano to mNearDuration.
+ if (mNear) {
+ mNearDuration += timestampNano - mNearStartTimeNano;
+ }
+
+ // if the new state is near, set mNearStartTimeNano equal to this moment.
+ if (near) {
+ mNearStartTimeNano = timestampNano;
+ }
+ }
+ mNear = near;
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type) {
+ return ProximityEvaluator.evaluate(mAverageNear, type);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java
new file mode 100644
index 0000000..91002bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class ProximityEvaluator {
+ public static float evaluate(float value, int type) {
+ float evaluation = 0.0f;
+ float threshold = 0.1f;
+ if (type == Classifier.QUICK_SETTINGS) {
+ threshold = 1.0f;
+ }
+ if (value >= threshold) evaluation += 2.0;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
new file mode 100644
index 0000000..d544a3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.view.MotionEvent;
+
+import java.lang.Math;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A classifier which for each point from a stroke, it creates a point on plane with coordinates
+ * (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE)
+ * and then it calculates the angle variance of these points like the class
+ * {@link AnglesClassifier} (without splitting it into two parts). The classifier ignores
+ * the last point of a stroke because the UP event comes in with some delay and this ruins the
+ * smoothness of this curve. Additionally, the classifier classifies calculates the percentage of
+ * angles which value is in [PI - ANGLE_DEVIATION, 2* PI) interval. The reason why the classifier
+ * does that is because the speed of a good stroke is most often increases, so most of these angels
+ * should be in this interval.
+ */
+public class SpeedAnglesClassifier extends StrokeClassifier {
+ private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
+
+ public SpeedAnglesClassifier(ClassifierData classifierData) {
+ mClassifierData = classifierData;
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mStrokeMap.clear();
+ }
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+
+ if (mStrokeMap.get(stroke) == null) {
+ mStrokeMap.put(stroke, new Data());
+ }
+
+ if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL
+ && !(action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+ mStrokeMap.get(stroke).addPoint(
+ stroke.getPoints().get(stroke.getPoints().size() - 1));
+ }
+ }
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ Data data = mStrokeMap.get(stroke);
+ return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance())
+ + SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
+ }
+
+ private static class Data {
+ private final float DURATION_SCALE = 1e8f;
+ private final float LENGTH_SCALE = 1.0f;
+ private final float ANGLE_DEVIATION = (float) Math.PI / 10.0f;
+
+ private List<Point> mLastThreePoints = new ArrayList<>();
+ private Point mPreviousPoint;
+ private float mPreviousAngle;
+ private float mSumSquares;
+ private float mSum;
+ private float mCount;
+ private float mDist;
+ private float mAnglesCount;
+ private float mAcceleratingAngles;
+
+ public Data() {
+ mPreviousPoint = null;
+ mPreviousAngle = (float) Math.PI;
+ mSumSquares = 0.0f;
+ mSum = 0.0f;
+ mCount = 1.0f;
+ mDist = 0.0f;
+ mAnglesCount = mAcceleratingAngles = 0.0f;
+ }
+
+ public void addPoint(Point point) {
+ if (mPreviousPoint != null) {
+ mDist += mPreviousPoint.dist(point);
+ }
+
+ mPreviousPoint = point;
+ Point speedPoint = new Point((float) point.timeOffsetNano / DURATION_SCALE,
+ mDist / LENGTH_SCALE);
+
+ // Checking if the added point is different than the previously added point
+ // Repetitions are being ignored so that proper angles are calculated.
+ if (mLastThreePoints.isEmpty()
+ || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(speedPoint)) {
+ mLastThreePoints.add(speedPoint);
+ if (mLastThreePoints.size() == 4) {
+ mLastThreePoints.remove(0);
+
+ float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
+ mLastThreePoints.get(2));
+
+ mAnglesCount++;
+ if (angle >= (float) Math.PI - ANGLE_DEVIATION) {
+ mAcceleratingAngles++;
+ }
+
+ float difference = angle - mPreviousAngle;
+ mSum += difference;
+ mSumSquares += difference * difference;
+ mCount += 1.0;
+ mPreviousAngle = angle;
+ }
+ }
+ }
+
+ public float getAnglesVariance() {
+ return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
+ }
+
+ public float getAnglesPercentage() {
+ if (mAnglesCount == 0.0f) {
+ return 1.0f;
+ }
+ return (mAcceleratingAngles) / mAnglesCount;
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
new file mode 100644
index 0000000..2a45fa3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class SpeedAnglesPercentageEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 1.00) evaluation++;
+ if (value < 0.95) evaluation++;
+ if (value < 0.90) evaluation++;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
new file mode 100644
index 0000000..81b78c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier that looks at the speed of the stroke. It calculates the speed of a stroke in
+ * inches per second.
+ */
+public class SpeedClassifier extends StrokeClassifier {
+ private final float NANOS_TO_SECONDS = 1e9f;
+
+ public SpeedClassifier(ClassifierData classifierData) {
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(int type, Stroke stroke) {
+ float duration = (float) stroke.getDurationNanos() / NANOS_TO_SECONDS;
+ if (duration == 0.0f) {
+ return SpeedEvaluator.evaluate(0.0f);
+ }
+ return SpeedEvaluator.evaluate(stroke.getTotalLength() / duration);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
new file mode 100644
index 0000000..c0e4a2d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class SpeedEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 4.0 || value > 35.0) evaluation += 1.0;
+ if (value < 2.2) evaluation += 1.0;
+ if (value > 50.0) evaluation += 1.0;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
new file mode 100644
index 0000000..349aa9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class SpeedRatioEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value > 9.0) ++evaluation;
+ if (value > 18.0) ++evaluation;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
new file mode 100644
index 0000000..8f9a7e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class SpeedVarianceEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value > 0.06) evaluation += 1.0;
+ if (value > 0.15) evaluation += 1.0;
+ if (value > 0.3) evaluation += 1.0;
+ if (value > 0.6) evaluation += 1.0;
+ return evaluation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
new file mode 100644
index 0000000..fb04d3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import java.util.ArrayList;
+
+/**
+ * Contains data about a stroke (a single trace, all the events from a given id from the
+ * DOWN/POINTER_DOWN event till the UP/POINTER_UP/CANCEL event.)
+ */
+public class Stroke {
+ private final float NANOS_TO_SECONDS = 1e9f;
+
+ private ArrayList<Point> mPoints = new ArrayList<>();
+ private long mStartTimeNano;
+ private long mEndTimeNano;
+ private float mLength;
+ private final float mDpi;
+
+ public Stroke(long eventTimeNano, float dpi) {
+ mDpi = dpi;
+ mStartTimeNano = mEndTimeNano = eventTimeNano;
+ }
+
+ public void addPoint(float x, float y, long eventTimeNano) {
+ mEndTimeNano = eventTimeNano;
+ Point point = new Point(x / mDpi, y / mDpi, eventTimeNano - mStartTimeNano);
+ if (!mPoints.isEmpty()) {
+ mLength += mPoints.get(mPoints.size() - 1).dist(point);
+ }
+ mPoints.add(point);
+ }
+
+ public int getCount() {
+ return mPoints.size();
+ }
+
+ public float getTotalLength() {
+ return mLength;
+ }
+
+ public float getEndPointLength() {
+ return mPoints.get(0).dist(mPoints.get(mPoints.size() - 1));
+ }
+
+ public long getDurationNanos() {
+ return mEndTimeNano - mStartTimeNano;
+ }
+
+ public float getDurationSeconds() {
+ return (float) getDurationNanos() / NANOS_TO_SECONDS;
+ }
+
+ public ArrayList<Point> getPoints() {
+ return mPoints;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java
new file mode 100644
index 0000000..5da392f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * An abstract class for classifiers which classify each stroke separately.
+ */
+public abstract class StrokeClassifier extends Classifier {
+
+ /**
+ * @param type the type of action for which this method is called
+ * @param stroke the stroke for which the evaluation will be calculated
+ * @return a non-negative value which is used to determine whether this a false touch; the
+ * bigger the value the greater the chance that this a false touch
+ */
+ public abstract float getFalseTouchEvaluation(int type, Stroke stroke);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0be3069..2ea1e43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -68,13 +68,15 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SystemUI;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.phone.FingerprintUnlockController;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -153,6 +155,7 @@
private static final int NOTIFY_STARTED_WAKING_UP = 21;
private static final int NOTIFY_SCREEN_TURNED_ON = 22;
private static final int NOTIFY_SCREEN_TURNED_OFF = 23;
+ private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 24;
/**
* The default amount of time we stay awake (used for all key input)
@@ -650,6 +653,7 @@
final boolean lockImmediately =
mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser)
|| !mLockPatternUtils.isSecure(currentUser);
+ long timeout = getLockTimeout();
if (mExitSecureCallback != null) {
if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
@@ -664,9 +668,9 @@
}
} else if (mShowing) {
mPendingReset = true;
- } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT
+ } else if ((why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT && timeout > 0)
|| (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) {
- doKeyguardLaterLocked();
+ doKeyguardLaterLocked(timeout);
} else if (!mLockPatternUtils.isLockScreenDisabled(currentUser)) {
mPendingLock = true;
}
@@ -675,6 +679,8 @@
playSounds(true);
}
}
+ KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedGoingToSleep(why);
+ notifyStartedGoingToSleep();
}
public void onFinishedGoingToSleep(int why) {
@@ -700,7 +706,7 @@
KeyguardUpdateMonitor.getInstance(mContext).dispatchFinishedGoingToSleep(why);
}
- private void doKeyguardLaterLocked() {
+ private long getLockTimeout() {
// if the screen turned off because of timeout or the user hit the power button
// and we don't need to lock immediately, set an alarm
// to enable it a little bit later (i.e, give the user a chance
@@ -729,23 +735,30 @@
} else {
timeout = lockAfterTimeout;
}
+ return timeout;
+ }
- if (timeout <= 0) {
- // Lock now
+ private void doKeyguardLaterLocked() {
+ long timeout = getLockTimeout();
+ if (timeout == 0) {
doKeyguardLocked(null);
} else {
- // Lock in the future
- long when = SystemClock.elapsedRealtime() + timeout;
- Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
- intent.putExtra("seq", mDelayedShowingSequence);
- PendingIntent sender = PendingIntent.getBroadcast(mContext,
- 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
- if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
- + mDelayedShowingSequence);
+ doKeyguardLaterLocked(timeout);
}
}
+ private void doKeyguardLaterLocked(long timeout) {
+ // Lock in the future
+ long when = SystemClock.elapsedRealtime() + timeout;
+ Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
+ intent.putExtra("seq", mDelayedShowingSequence);
+ PendingIntent sender = PendingIntent.getBroadcast(mContext,
+ 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
+ if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
+ + mDelayedShowingSequence);
+ }
+
private void cancelDoKeyguardLaterLocked() {
mDelayedShowingSequence++;
}
@@ -908,9 +921,27 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
}
+ } else if (!isSecure()) {
+
+ // Keyguard is not secure, no need to do anything, and we don't need to reshow
+ // the Keyguard after the client releases the Keyguard lock.
+ mExternallyEnabled = true;
+ mNeedToReshowWhenReenabled = false;
+ updateInputRestricted();
+ try {
+ callback.onKeyguardExitResult(true);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
} else {
- mExitSecureCallback = callback;
- verifyUnlockLocked();
+
+ // Since we prevent apps from hiding the Keyguard if we are secure, this should be
+ // a no-op as well.
+ try {
+ callback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
}
}
}
@@ -1096,6 +1127,11 @@
mHandler.sendEmptyMessage(VERIFY_UNLOCK);
}
+ private void notifyStartedGoingToSleep() {
+ if (DEBUG) Log.d(TAG, "notifyStartedGoingToSleep");
+ mHandler.sendEmptyMessage(NOTIFY_STARTED_GOING_TO_SLEEP);
+ }
+
private void notifyFinishedGoingToSleep() {
if (DEBUG) Log.d(TAG, "notifyFinishedGoingToSleep");
mHandler.sendEmptyMessage(NOTIFY_FINISHED_GOING_TO_SLEEP);
@@ -1207,6 +1243,9 @@
case VERIFY_UNLOCK:
handleVerifyUnlock();
break;
+ case NOTIFY_STARTED_GOING_TO_SLEEP:
+ handleNotifyStartedGoingToSleep();
+ break;
case NOTIFY_FINISHED_GOING_TO_SLEEP:
handleNotifyFinishedGoingToSleep();
break;
@@ -1242,7 +1281,7 @@
case START_KEYGUARD_EXIT_ANIM:
StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
- LockedPhoneAnalytics.getInstance(mContext).onSucccessfulUnlock();
+ FalsingManager.getInstance(mContext).onSucccessfulUnlock();
break;
case KEYGUARD_DONE_PENDING_TIMEOUT:
Log.w(TAG, "Timeout while waiting for activity drawn!");
@@ -1544,6 +1583,13 @@
}
}
+ private void handleNotifyStartedGoingToSleep() {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep");
+ mStatusBarKeyguardViewManager.onStartedGoingToSleep();
+ }
+ }
+
/**
* Handle message sent by {@link #notifyFinishedGoingToSleep()}
* @see #NOTIFY_FINISHED_GOING_TO_SLEEP
@@ -1641,6 +1687,30 @@
return mViewMediatorCallback;
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print(" mSystemReady: "); pw.println(mSystemReady);
+ pw.print(" mBootCompleted: "); pw.println(mBootCompleted);
+ pw.print(" mBootSendUserPresent: "); pw.println(mBootSendUserPresent);
+ pw.print(" mExternallyEnabled: "); pw.println(mExternallyEnabled);
+ pw.print(" mNeedToReshowWhenReenabled: "); pw.println(mNeedToReshowWhenReenabled);
+ pw.print(" mShowing: "); pw.println(mShowing);
+ pw.print(" mInputRestricted: "); pw.println(mInputRestricted);
+ pw.print(" mOccluded: "); pw.println(mOccluded);
+ pw.print(" mDelayedShowingSequence: "); pw.println(mDelayedShowingSequence);
+ pw.print(" mExitSecureCallback: "); pw.println(mExitSecureCallback);
+ pw.print(" mDeviceInteractive: "); pw.println(mDeviceInteractive);
+ pw.print(" mGoingToSleep: "); pw.println(mGoingToSleep);
+ pw.print(" mHiding: "); pw.println(mHiding);
+ pw.print(" mWaitingUntilKeyguardVisible: "); pw.println(mWaitingUntilKeyguardVisible);
+ pw.print(" mKeyguardDonePending: "); pw.println(mKeyguardDonePending);
+ pw.print(" mHideAnimationRun: "); pw.println(mHideAnimationRun);
+ pw.print(" mPendingReset: "); pw.println(mPendingReset);
+ pw.print(" mPendingLock: "); pw.println(mPendingLock);
+ pw.print(" mWakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
+ pw.print(" mDrawnCallback: "); pw.println(mDrawnCallback);
+ }
+
private static class StartKeyguardExitAnimParams {
long startTime;
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 7f68e29..f39f302 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -160,7 +160,7 @@
throw new SecurityException("Async playback only available from system UID.");
}
if (UserHandle.ALL.equals(user)) {
- user = UserHandle.OWNER;
+ user = UserHandle.SYSTEM;
}
mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
index 87194fb..b157275 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles;
import com.android.systemui.qs.QSTileView;
+import com.android.systemui.statusbar.policy.WifiIcons;
/** Quick settings tile: Wifi **/
public class QWifiTile extends WifiTile {
@@ -29,4 +30,23 @@
public int getTileType() {
return QSTileView.QS_TYPE_QUICK;
}
+
+ @Override
+ protected void handleUpdateState(SignalState state, Object arg) {
+ super.handleUpdateState(state, arg);
+
+ CallbackInfo cb = (CallbackInfo) arg;
+ if (cb == null) {
+ cb = mSignalCallback.mInfo;
+ }
+ boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
+
+ if (state.enabled && wifiConnected) {
+ // Only show full signal here.
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][4]);
+ }
+ // No activity in the quick toggle.
+ state.activityIn = false;
+ state.activityOut = false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 3295e14..91e0218 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -48,7 +48,7 @@
private final WifiDetailAdapter mDetailAdapter;
private final QSTile.SignalState mStateBeforeClick = newTileState();
- private final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
+ protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
private final boolean mAlwaysDetail;
@@ -200,7 +200,7 @@
return string;
}
- private static final class CallbackInfo {
+ protected static final class CallbackInfo {
boolean enabled;
boolean connected;
int wifiSignalIconId;
@@ -223,7 +223,7 @@
}
}
- private final class WifiSignalCallback extends SignalCallbackAdapter {
+ protected final class WifiSignalCallback extends SignalCallbackAdapter {
final CallbackInfo mInfo = new CallbackInfo();
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index a4acf83..9781664 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -29,8 +29,6 @@
}
public static class DebugFlags {
- // Enable this with any other debug flag to see more info
- public static final boolean Verbose = false;
public static class App {
// Enables debug drawing for the transition thumbnail
@@ -39,14 +37,8 @@
public static final boolean EnableTaskFiltering = false;
// Enables dismiss-all
public static final boolean EnableDismissAll = false;
- // Enables debug mode
- public static final boolean EnableDebugMode = false;
- // Enables the search bar layout
- public static final boolean EnableSearchLayout = true;
// Enables the thumbnail alpha on the front-most task
public static final boolean EnableThumbnailAlphaOnFrontmost = false;
- // Enables all system stacks to show up in the same recents stack
- public static final boolean EnableMultiStackToSingleStack = true;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
// Enables the simulated task affiliations
@@ -65,7 +57,6 @@
public static class Values {
public static class App {
public static int AppWidgetHostId = 1024;
- public static String DebugModeVersion = "A";
}
public static class TaskStackView {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 0cfa7a1..3e23ed8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -38,7 +38,6 @@
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -116,7 +115,7 @@
/** Preloads the next task */
public void run() {
// Temporarily skip this if multi stack is enabled
- if (mConfig.multiStackEnabled) return;
+ if (mConfig.multiWindowEnabled) return;
RecentsConfiguration config = RecentsConfiguration.getInstance();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
@@ -151,7 +150,8 @@
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER:
- visibilityChanged(intent.getBooleanExtra(EXTRA_RECENTS_VISIBILITY, false));
+ visibilityChanged(context,
+ intent.getBooleanExtra(EXTRA_RECENTS_VISIBILITY, false));
break;
case ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER:
onStartScreenPinning(context);
@@ -160,7 +160,6 @@
}
}
- static RecentsComponent.Callbacks sRecentsComponentCallbacks;
static RecentsTaskLoadPlan sInstanceLoadPlan;
static Recents sInstance;
@@ -176,7 +175,6 @@
// Task launching
RecentsConfiguration mConfig;
- Rect mWindowRect = new Rect();
Rect mTaskStackBounds = new Rect();
Rect mSystemInsets = new Rect();
TaskViewTransform mTmpTransform = new TaskViewTransform();
@@ -372,9 +370,9 @@
if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
- TaskStack top = sInstanceLoadPlan.getAllTaskStacks().get(0);
- if (top.getTaskCount() > 0) {
- preCacheThumbnailTransitionBitmapAsync(topTask, top, mDummyStackView,
+ TaskStack stack = sInstanceLoadPlan.getTaskStack();
+ if (stack.getTaskCount() > 0) {
+ preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView,
topTaskHome.value);
}
}
@@ -388,16 +386,10 @@
void showRelativeAffiliatedTask(boolean showNextTask) {
// Return early if there is no focused stack
int focusedStackId = mSystemServicesProxy.getFocusedStack();
- TaskStack focusedStack = null;
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
loader.preloadTasks(plan, true /* isTopTaskHome */);
- if (mConfig.multiStackEnabled) {
- if (focusedStackId < 0) return;
- focusedStack = plan.getTaskStack(focusedStackId);
- } else {
- focusedStack = plan.getAllTaskStacks().get(0);
- }
+ TaskStack focusedStack = plan.getTaskStack();
// Return early if there are no tasks in the focused stack
if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
@@ -502,24 +494,26 @@
/** Prepares the header bar layout. */
void reloadHeaderBarLayout() {
Resources res = mContext.getResources();
- mWindowRect = mSystemServicesProxy.getWindowRect();
+ Rect windowRect = mSystemServicesProxy.getWindowRect();
+
mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
- mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
+ mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy);
+ mConfig.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect());
mConfig.updateOnConfigurationChange();
Rect searchBarBounds = new Rect();
// Try and pre-emptively bind the search widget on startup to ensure that we
// have the right thumbnail bounds to animate to.
// Note: We have to reload the widget id before we get the task stack bounds below
if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
- mConfig.getSearchBarBounds(mWindowRect.width(), mWindowRect.height(),
+ mConfig.getSearchBarBounds(windowRect,
mStatusBarHeight, searchBarBounds);
}
- mConfig.getAvailableTaskStackBounds(mWindowRect.width(), mWindowRect.height(),
+ mConfig.getAvailableTaskStackBounds(windowRect,
mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), searchBarBounds,
mTaskStackBounds);
- if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
+ if (mConfig.hasTransposedNavBar) {
mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
} else {
mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
@@ -531,7 +525,7 @@
TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
Rect taskStackBounds = new Rect(mTaskStackBounds);
taskStackBounds.bottom -= mSystemInsets.bottom;
- algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
+ algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
Rect taskViewSize = algo.getUntransformedTaskViewSize();
int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
synchronized (mHeaderBarLock) {
@@ -540,6 +534,7 @@
mHeaderBar.measure(
View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
+ // TODO: may not be needed
mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
}
}
@@ -740,7 +735,10 @@
/** Starts the recents activity */
void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
+ // Don't reinitialize the configuration completely here, since it has the wrong context,
+ // only update the parts that we can get from any context
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ config.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect());
if (sInstanceLoadPlan == null) {
// Create a new load plan if onPreloadRecents() was never triggered
@@ -749,10 +747,9 @@
// Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
// For multi-stack we need to figure out where each of the tasks are going.
- if (mConfig.multiStackEnabled) {
+ if (mConfig.multiWindowEnabled) {
loader.preloadTasks(sInstanceLoadPlan, true);
- ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
- TaskStack stack = stacks.get(0);
+ TaskStack stack = sInstanceLoadPlan.getTaskStack();
mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
mDummyStackView.computeStackVisibilityReport();
@@ -765,8 +762,7 @@
if (!sInstanceLoadPlan.hasTasks()) {
loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
}
- ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
- TaskStack stack = stacks.get(0);
+ TaskStack stack = sInstanceLoadPlan.getTaskStack();
// Prepare the dummy stack for the transition
mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
@@ -818,15 +814,16 @@
ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
// Update the configuration based on the launch options
- mConfig.launchedFromHome = fromSearchHome || fromHome;
- mConfig.launchedFromSearchHome = fromSearchHome;
- mConfig.launchedFromAppWithThumbnail = fromThumbnail;
- mConfig.launchedToTaskId = (topTask != null) ? topTask.id : -1;
- mConfig.launchedWithAltTab = mTriggeredFromAltTab;
- mConfig.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
- mConfig.launchedNumVisibleTasks = vr.numVisibleTasks;
- mConfig.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
- mConfig.launchedHasConfigurationChanged = false;
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ launchState.launchedFromHome = fromSearchHome || fromHome;
+ launchState.launchedFromSearchHome = fromSearchHome;
+ launchState.launchedFromAppWithThumbnail = fromThumbnail;
+ launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+ launchState.launchedWithAltTab = mTriggeredFromAltTab;
+ launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
+ launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
+ launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
+ launchState.launchedHasConfigurationChanged = false;
Intent intent = new Intent(sToggleRecentsAction);
intent.setClassName(sRecentsPackage, sRecentsActivity);
@@ -841,18 +838,12 @@
mCanReuseTaskStackViews = true;
}
- /** Sets the RecentsComponent callbacks. */
- @Override
- public void setCallback(RecentsComponent.Callbacks cb) {
- sRecentsComponentCallbacks = cb;
- }
-
/** Notifies the callbacks that the visibility of Recents has changed. */
@ProxyFromAnyToSystemUser
public static void notifyVisibilityChanged(Context context, SystemServicesProxy ssp,
boolean visible) {
if (ssp.isForegroundUserSystem()) {
- visibilityChanged(visible);
+ visibilityChanged(context, visible);
} else {
Intent intent = createLocalBroadcastIntent(context,
ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
@@ -860,9 +851,13 @@
context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
}
}
- static void visibilityChanged(boolean visible) {
- if (sRecentsComponentCallbacks != null) {
- sRecentsComponentCallbacks.onVisibilityChanged(visible);
+ static void visibilityChanged(Context context, boolean visible) {
+ // For the primary user, the context for the SystemUI component is the SystemUIApplication
+ SystemUIApplication app = (SystemUIApplication)
+ getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
+ PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
+ if (statusBar != null) {
+ statusBar.updateRecentsVisibility(visible);
}
}
@@ -880,7 +875,7 @@
static void onStartScreenPinning(Context context) {
// For the primary user, the context for the SystemUI component is the SystemUIApplication
SystemUIApplication app = (SystemUIApplication)
- getInstanceAndStartIfNeeded(context).mContext;
+ getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
if (statusBar != null) {
statusBar.showScreenPinningRequest(false);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d0876fa..9ce6b2c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -31,20 +31,15 @@
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewStub;
-import android.widget.Toast;
-
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.recents.misc.Console;
-import com.android.systemui.recents.misc.DebugTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.DebugOverlayView;
import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.recents.views.ViewAnimation;
@@ -56,8 +51,7 @@
* The main Recents activity that is started from AlternateRecentsComponent.
*/
public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
- RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
- DebugOverlayView.DebugOverlayViewCallbacks {
+ RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
RecentsConfiguration mConfig;
long mLastTabKeyEventTime;
@@ -66,9 +60,7 @@
RecentsView mRecentsView;
SystemBarScrimViews mScrimViews;
ViewStub mEmptyViewStub;
- ViewStub mDebugOverlayStub;
View mEmptyView;
- DebugOverlayView mDebugOverlay;
// Resize task debug
RecentsResizeTaskDialog mResizeTaskDebugDialog;
@@ -175,16 +167,6 @@
}
};
- /**
- * A custom debug trigger to listen for a debug key chord.
- */
- final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() {
- @Override
- public void run() {
- onDebugModeTriggered();
- }
- });
-
/** Updates the set of recent tasks */
void updateRecentsTasks() {
// If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
@@ -196,19 +178,20 @@
}
// Start loading tasks according to the load plan
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
if (!plan.hasTasks()) {
- loader.preloadTasks(plan, mConfig.launchedFromHome);
+ loader.preloadTasks(plan, launchState.launchedFromHome);
}
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
- loadOpts.runningTaskId = mConfig.launchedToTaskId;
- loadOpts.numVisibleTasks = mConfig.launchedNumVisibleTasks;
- loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;
+ loadOpts.runningTaskId = launchState.launchedToTaskId;
+ loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+ loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, plan, loadOpts);
- ArrayList<TaskStack> stacks = plan.getAllTaskStacks();
- mConfig.launchedWithNoRecentTasks = !plan.hasTasks();
- if (!mConfig.launchedWithNoRecentTasks) {
- mRecentsView.setTaskStacks(stacks);
+ TaskStack stack = plan.getTaskStack();
+ launchState.launchedWithNoRecentTasks = !plan.hasTasks();
+ if (!launchState.launchedWithNoRecentTasks) {
+ mRecentsView.setTaskStack(stack);
}
// Create the home intent runnable
@@ -218,32 +201,28 @@
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
ActivityOptions.makeCustomAnimation(this,
- mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
+ launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
R.anim.recents_to_launcher_enter,
- mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
+ launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
R.anim.recents_to_launcher_exit));
// Mark the task that is the launch target
- int taskStackCount = stacks.size();
int launchTaskIndexInStack = 0;
- if (mConfig.launchedToTaskId != -1) {
- for (int i = 0; i < taskStackCount; i++) {
- TaskStack stack = stacks.get(i);
- ArrayList<Task> tasks = stack.getTasks();
- int taskCount = tasks.size();
- for (int j = 0; j < taskCount; j++) {
- Task t = tasks.get(j);
- if (t.key.id == mConfig.launchedToTaskId) {
- t.isLaunchTarget = true;
- launchTaskIndexInStack = tasks.size() - j - 1;
- break;
- }
+ if (launchState.launchedToTaskId != -1) {
+ ArrayList<Task> tasks = stack.getTasks();
+ int taskCount = tasks.size();
+ for (int j = 0; j < taskCount; j++) {
+ Task t = tasks.get(j);
+ if (t.key.id == launchState.launchedToTaskId) {
+ t.isLaunchTarget = true;
+ launchTaskIndexInStack = tasks.size() - j - 1;
+ break;
}
}
}
// Update the top level view's visibilities
- if (mConfig.launchedWithNoRecentTasks) {
+ if (launchState.launchedWithNoRecentTasks) {
if (mEmptyView == null) {
mEmptyView = mEmptyViewStub.inflate();
}
@@ -264,13 +243,13 @@
mScrimViews.prepareEnterRecentsAnimation();
// Keep track of whether we launched from the nav bar button or via alt-tab
- if (mConfig.launchedWithAltTab) {
+ if (launchState.launchedWithAltTab) {
MetricsLogger.count(this, "overview_trigger_alttab", 1);
} else {
MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
}
// Keep track of whether we launched from an app or from home
- if (mConfig.launchedFromAppWithThumbnail) {
+ if (launchState.launchedFromAppWithThumbnail) {
MetricsLogger.count(this, "overview_source_app", 1);
// If from an app, track the stack index of the app in the stack (for affiliated tasks)
MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
@@ -278,16 +257,13 @@
MetricsLogger.count(this, "overview_source_home", 1);
}
// Keep track of the total stack task count
- int taskCount = 0;
- for (int i = 0; i < stacks.size(); i++) {
- TaskStack stack = stacks.get(i);
- taskCount += stack.getTaskCount();
- }
+ int taskCount = stack.getTaskCount();
MetricsLogger.histogram(this, "overview_task_count", taskCount);
}
/** Dismisses recents if we are already visible and the intent is to toggle the recents view */
boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// If we currently have filtered stacks, then unfilter those first
@@ -296,7 +272,7 @@
// If we have a focused Task, launch that Task now
if (mRecentsView.launchFocusedTask()) return true;
// If we launched from Home, then return to Home
- if (mConfig.launchedFromHome) {
+ if (launchState.launchedFromHome) {
dismissRecentsToHomeRaw(true);
return true;
}
@@ -346,7 +322,8 @@
// initialized
RecentsTaskLoader.initialize(this);
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
- mConfig = RecentsConfiguration.reinitialize(this, ssp);
+ mConfig = RecentsConfiguration.initialize(this, ssp);
+ mConfig.update(this, ssp, ssp.getWindowRect());
// Initialize the widget host (the host id is static and does not change)
mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
@@ -359,9 +336,7 @@
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
- mDebugOverlayStub = (ViewStub) findViewById(R.id.debug_overlay_stub);
- mScrimViews = new SystemBarScrimViews(this, mConfig);
- inflateDebugOverlay();
+ mScrimViews = new SystemBarScrimViews(this);
// Bind the search app widget when we first start up
mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
@@ -373,32 +348,16 @@
registerReceiver(mSystemBroadcastReceiver, filter);
}
- /** Inflates the debug overlay if debug mode is enabled. */
- void inflateDebugOverlay() {
- if (!Constants.DebugFlags.App.EnableDebugMode) return;
-
- if (mConfig.debugModeEnabled && mDebugOverlay == null) {
- // Inflate the overlay and seek bars
- mDebugOverlay = (DebugOverlayView) mDebugOverlayStub.inflate();
- mDebugOverlay.setCallbacks(this);
- mRecentsView.setDebugOverlay(mDebugOverlay);
- }
- }
-
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
-
- // Clear any debug rects
- if (mDebugOverlay != null) {
- mDebugOverlay.clear();
- }
}
@Override
protected void onStart() {
super.onStart();
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SystemServicesProxy ssp = loader.getSystemServicesProxy();
@@ -420,12 +379,13 @@
// If this is a new instance from a configuration change, then we have to manually trigger
// the enter animation state, or if recents was relaunched by AM, without going through
// the normal mechanisms
- boolean wasLaunchedByAm = !mConfig.launchedFromHome && !mConfig.launchedFromAppWithThumbnail;
- if (mConfig.launchedHasConfigurationChanged || wasLaunchedByAm) {
+ boolean wasLaunchedByAm = !launchState.launchedFromHome &&
+ !launchState.launchedFromAppWithThumbnail;
+ if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
onEnterAnimationTriggered();
}
- if (!mConfig.launchedHasConfigurationChanged) {
+ if (!launchState.launchedHasConfigurationChanged) {
mRecentsView.disableLayersForOneFrame();
}
}
@@ -443,6 +403,7 @@
protected void onStop() {
super.onStop();
MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SystemServicesProxy ssp = loader.getSystemServicesProxy();
Recents.notifyVisibilityChanged(this, ssp, false);
@@ -459,12 +420,12 @@
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not
// wait on the system to send a signal that was never queued.
- mConfig.launchedFromHome = false;
- mConfig.launchedFromSearchHome = false;
- mConfig.launchedFromAppWithThumbnail = false;
- mConfig.launchedToTaskId = -1;
- mConfig.launchedWithAltTab = false;
- mConfig.launchedHasConfigurationChanged = false;
+ launchState.launchedFromHome = false;
+ launchState.launchedFromSearchHome = false;
+ launchState.launchedFromAppWithThumbnail = false;
+ launchState.launchedToTaskId = -1;
+ launchState.launchedWithAltTab = false;
+ launchState.launchedHasConfigurationChanged = false;
}
@Override
@@ -516,8 +477,9 @@
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_TAB: {
+ int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
- mLastTabKeyEventTime) > mConfig.altTabKeyDelay;
+ mLastTabKeyEventTime) > altTabKeyDelay;
if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
// Focus the next task in the stack
final boolean backward = event.isShiftPressed();
@@ -545,8 +507,6 @@
default:
break;
}
- // Pass through the debug trigger
- mDebugTrigger.onKeyEvent(keyCode);
return super.onKeyDown(keyCode, event);
}
@@ -557,40 +517,10 @@
@Override
public void onBackPressed() {
- // Test mode where back does not do anything
- if (mConfig.debugModeEnabled) return;
-
// Dismiss Recents to the focused Task or Home
dismissRecentsToFocusedTaskOrHome(true);
}
- /** Called when debug mode is triggered */
- public void onDebugModeTriggered() {
- if (mConfig.developerOptionsEnabled) {
- if (Prefs.getBoolean(this, Prefs.Key.DEBUG_MODE_ENABLED, false /* boolean */)) {
- // Disable the debug mode
- Prefs.remove(this, Prefs.Key.DEBUG_MODE_ENABLED);
- mConfig.debugModeEnabled = false;
- inflateDebugOverlay();
- if (mDebugOverlay != null) {
- mDebugOverlay.disable();
- }
- } else {
- // Enable the debug mode
- Prefs.putBoolean(this, Prefs.Key.DEBUG_MODE_ENABLED, true);
- mConfig.debugModeEnabled = true;
- inflateDebugOverlay();
- if (mDebugOverlay != null) {
- mDebugOverlay.enable();
- }
- }
- Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
- (mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
- Toast.LENGTH_SHORT).show();
- }
- }
-
-
/**** RecentsResizeTaskDialog ****/
private RecentsResizeTaskDialog getResizeTaskDebugDialog() {
@@ -662,16 +592,4 @@
mRecentsView.setSearchBar(null);
}
}
-
- /**** DebugOverlayView.DebugOverlayViewCallbacks ****/
-
- @Override
- public void onPrimarySeekBarChanged(float progress) {
- // Do nothing
- }
-
- @Override
- public void onSecondarySeekBarChanged(float progress) {
- // Do nothing
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
new file mode 100644
index 0000000..e2e0e918
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+/**
+ * The launch state of the RecentsActivity.
+ *
+ * TODO: We will be refactoring this out RecentsConfiguration.
+ * Current Constraints:
+ * - needed in onStart() before onNewIntent()
+ * - needs to be reset when Recents is hidden
+ * - needs to be computed in Recents component
+ * - needs to be accessible by views
+ */
+public class RecentsActivityLaunchState {
+
+ public RecentsConfiguration mConfig;
+
+ public boolean launchedWithAltTab;
+ public boolean launchedWithNoRecentTasks;
+ public boolean launchedFromAppWithThumbnail;
+ public boolean launchedFromHome;
+ public boolean launchedFromSearchHome;
+ public boolean launchedReuseTaskStackViews;
+ public boolean launchedHasConfigurationChanged;
+ public int launchedToTaskId;
+ public int launchedNumVisibleTasks;
+ public int launchedNumVisibleThumbnails;
+
+ RecentsActivityLaunchState(RecentsConfiguration config) {
+ mConfig = config;
+ }
+
+ /** Called when the configuration has changed, and we want to reset any configuration specific
+ * members. */
+ public void updateOnConfigurationChange() {
+ // Reset this flag on configuration change to ensure that we recreate new task views
+ launchedReuseTaskStackViews = false;
+ // Set this flag to indicate that the configuration has changed since Recents last launched
+ launchedHasConfigurationChanged = true;
+ }
+
+ /** Returns whether the status bar scrim should be animated when shown for the first time. */
+ public boolean shouldAnimateStatusBarScrim() {
+ return launchedFromHome;
+ }
+
+ /** Returns whether the status bar scrim should be visible. */
+ public boolean hasStatusBarScrim() {
+ return !launchedWithNoRecentTasks;
+ }
+
+ /** Returns whether the nav bar scrim should be animated when shown for the first time. */
+ public boolean shouldAnimateNavBarScrim() {
+ return true;
+ }
+
+ /** Returns whether the nav bar scrim should be visible. */
+ public boolean hasNavBarScrim() {
+ // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
+ return !launchedWithNoRecentTasks && mConfig.hasTransposedNavBar;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index dfe7e96..52b9521 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -16,27 +16,29 @@
package com.android.systemui.recents;
-import android.app.ActivityManager;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.SystemServicesProxy;
-
-/** A static Recents configuration for the current context
- * NOTE: We should not hold any references to a Context from a static instance */
+/**
+ * Application resources that can be retrieved from the application context and are not specifically
+ * tied to the current activity.
+ */
public class RecentsConfiguration {
static RecentsConfiguration sInstance;
- static int sPrevConfigurationHashCode;
+
+ private static final int LARGE_SCREEN_MIN_DP = 600;
+ private static final int XLARGE_SCREEN_MIN_DP = 720;
+
+ // Variables that are used for global calculations
+ private static final float STACK_SIDE_PADDING_PHONES_PCT = 0.03333f;
+ private static final float STACK_SIZE_PADDING_TABLETS_PCT = 0.075f;
+ private static final float STACK_SIZE_PADDING_LARGE_TABLETS_PCT = 0.15f;
+ private static final int SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS = 64;
+ private static final int SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS = 72;
/** Levels of svelte in increasing severity/austerity. */
// No svelting.
@@ -50,123 +52,81 @@
// Disable all thumbnail loading.
public static final int SVELTE_DISABLE_LOADING = 3;
- /** Interpolators */
- public Interpolator fastOutSlowInInterpolator;
- public Interpolator fastOutLinearInInterpolator;
- public Interpolator linearOutSlowInInterpolator;
- public Interpolator quintOutInterpolator;
+ // Launch states
+ public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState(this);
- /** Filtering */
- public int filteringCurrentViewsAnimDuration;
- public int filteringNewViewsAnimDuration;
-
- /** Insets */
- public Rect systemInsets = new Rect();
- public Rect displayRect = new Rect();
-
- /** Layout */
- boolean isLandscape;
+ // TODO: Values determined by the current context, needs to be refactored into something that is
+ // agnostic of the activity context, but still calculable from the Recents component for
+ // the transition into recents
boolean hasTransposedSearchBar;
boolean hasTransposedNavBar;
-
- /** Loading */
- public int maxNumTasksToLoad;
-
- /** Search bar */
- public int searchBarSpaceHeightPx;
-
- /** Task stack */
- public int taskStackScrollDuration;
- public int taskStackMaxDim;
- public int taskStackTopPaddingPx;
- public int dismissAllButtonSizePx;
public float taskStackWidthPaddingPct;
- public float taskStackOverscrollPct;
- /** Transitions */
- public int transitionEnterFromAppDelay;
- public int transitionEnterFromHomeDelay;
-
- /** Task view animation and styles */
- public int taskViewEnterFromAppDuration;
- public int taskViewEnterFromHomeDuration;
- public int taskViewEnterFromHomeStaggerDelay;
- public int taskViewExitToAppDuration;
- public int taskViewExitToHomeDuration;
- public int taskViewRemoveAnimDuration;
- public int taskViewRemoveAnimTranslationXPx;
- public int taskViewTranslationZMinPx;
- public int taskViewTranslationZMaxPx;
- public int taskViewRoundedCornerRadiusPx;
- public int taskViewHighlightPx;
- public int taskViewAffiliateGroupEnterOffsetPx;
- public float taskViewThumbnailAlpha;
-
- /** Task bar colors */
- public int taskBarViewDefaultBackgroundColor;
- public int taskBarViewLightTextColor;
- public int taskBarViewDarkTextColor;
- public int taskBarViewHighlightColor;
- public float taskBarViewAffiliationColorMinAlpha;
-
- /** Task bar size & animations */
- public int taskBarHeight;
- public int taskBarDismissDozeDelaySeconds;
-
- /** Nav bar scrim */
- public int navBarScrimEnterDuration;
-
- /** Launch states */
- public boolean launchedWithAltTab;
- public boolean launchedWithNoRecentTasks;
- public boolean launchedFromAppWithThumbnail;
- public boolean launchedFromHome;
- public boolean launchedFromSearchHome;
- public boolean launchedReuseTaskStackViews;
- public boolean launchedHasConfigurationChanged;
- public int launchedToTaskId;
- public int launchedNumVisibleTasks;
- public int launchedNumVisibleThumbnails;
+ // Since the positions in Recents has to be calculated globally (before the RecentsActivity
+ // starts), we need to calculate some resource values ourselves, instead of relying on framework
+ // resources.
+ public final boolean isLargeScreen;
+ public final boolean isXLargeScreen;
+ public final int smallestWidth;
/** Misc **/
public boolean useHardwareLayers;
- public int altTabKeyDelay;
public boolean fakeShadows;
+ public int svelteLevel;
+ public int searchBarSpaceHeightPx;
/** Dev options and global settings */
- public boolean multiStackEnabled;
+ public boolean multiWindowEnabled;
public boolean lockToAppEnabled;
- public boolean developerOptionsEnabled;
- public boolean debugModeEnabled;
- public int svelteLevel;
/** Private constructor */
- private RecentsConfiguration(Context context) {
- // Properties that don't have to be reloaded with each configuration change can be loaded
- // here.
+ private RecentsConfiguration(Context context, SystemServicesProxy ssp) {
+ // Load only resources that can not change after the first load either through developer
+ // settings or via multi window
+ Context appContext = context.getApplicationContext();
+ Resources res = appContext.getResources();
+ useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
+ fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
+ svelteLevel = res.getInteger(R.integer.recents_svelte_level);
- // Interpolators
- fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
- fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_linear_in);
- linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.linear_out_slow_in);
- quintOutInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.decelerate_quint);
+ float density = context.getResources().getDisplayMetrics().density;
+ smallestWidth = ssp.getDeviceSmallestWidth();
+ isLargeScreen = smallestWidth >= (int) (density * LARGE_SCREEN_MIN_DP);
+ isXLargeScreen = smallestWidth >= (int) (density * XLARGE_SCREEN_MIN_DP);
+ searchBarSpaceHeightPx = isLargeScreen ?
+ (int) (density * SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS) :
+ (int) (density * SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS);
+ if (isLargeScreen) {
+ taskStackWidthPaddingPct = STACK_SIZE_PADDING_TABLETS_PCT;
+ } else if (isXLargeScreen) {
+ taskStackWidthPaddingPct = STACK_SIZE_PADDING_LARGE_TABLETS_PCT;
+ } else {
+ taskStackWidthPaddingPct = STACK_SIDE_PADDING_PHONES_PCT;
+ }
+ }
+
+ /**
+ * Updates the configuration based on the current state of the system
+ */
+ void update(Context context, SystemServicesProxy ssp, Rect windowRect) {
+ // Only update resources that can change after the first load, either through developer
+ // settings or via multi window
+ lockToAppEnabled = ssp.getSystemSetting(context,
+ Settings.System.LOCK_TO_APP_ENABLED) != 0;
+ multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
+
+ // Recompute some values based on the given state, since we can not rely on the resource
+ // system to get certain values.
+ boolean isLandscape = windowRect.width() > windowRect.height();
+ hasTransposedNavBar = isLandscape && isLargeScreen && !isXLargeScreen;
+ hasTransposedSearchBar = isLandscape && isLargeScreen && !isXLargeScreen;
}
/** Updates the configuration to the current context */
- public static RecentsConfiguration reinitialize(Context context, SystemServicesProxy ssp) {
+ public static RecentsConfiguration initialize(Context context, SystemServicesProxy ssp) {
if (sInstance == null) {
- sInstance = new RecentsConfiguration(context);
+ sInstance = new RecentsConfiguration(context, ssp);
}
- int configHashCode = context.getResources().getConfiguration().hashCode();
- if (sPrevConfigurationHashCode != configHashCode) {
- sInstance.update(context);
- sPrevConfigurationHashCode = configHashCode;
- }
- sInstance.updateOnReinitialize(context, ssp);
return sInstance;
}
@@ -175,159 +135,37 @@
return sInstance;
}
- /** Updates the state, given the specified context */
- void update(Context context) {
- Resources res = context.getResources();
- DisplayMetrics dm = res.getDisplayMetrics();
-
- // Debug mode
- debugModeEnabled = Prefs.getBoolean(context, Prefs.Key.DEBUG_MODE_ENABLED,
- false /* defaultValue */);
- if (debugModeEnabled) {
- Console.Enabled = true;
- }
-
- // Layout
- isLandscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
- hasTransposedSearchBar = res.getBoolean(R.bool.recents_has_transposed_search_bar);
- hasTransposedNavBar = res.getBoolean(R.bool.recents_has_transposed_nav_bar);
-
- // Insets
- displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
-
- // Filtering
- filteringCurrentViewsAnimDuration =
- res.getInteger(R.integer.recents_filter_animate_current_views_duration);
- filteringNewViewsAnimDuration =
- res.getInteger(R.integer.recents_filter_animate_new_views_duration);
-
- // Loading
- maxNumTasksToLoad = ActivityManager.getMaxRecentTasksStatic();
-
- // Search Bar
- searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-
- // Task stack
- taskStackScrollDuration =
- res.getInteger(R.integer.recents_animate_task_stack_scroll_duration);
- taskStackWidthPaddingPct = res.getFloat(R.dimen.recents_stack_width_padding_percentage);
- taskStackOverscrollPct = res.getFloat(R.dimen.recents_stack_overscroll_percentage);
- taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
- taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
- dismissAllButtonSizePx = res.getDimensionPixelSize(R.dimen.recents_dismiss_all_button_size);
-
- // Transition
- transitionEnterFromAppDelay =
- res.getInteger(R.integer.recents_enter_from_app_transition_duration);
- transitionEnterFromHomeDelay =
- res.getInteger(R.integer.recents_enter_from_home_transition_duration);
-
- // Task view animation and styles
- taskViewEnterFromAppDuration =
- res.getInteger(R.integer.recents_task_enter_from_app_duration);
- taskViewEnterFromHomeDuration =
- res.getInteger(R.integer.recents_task_enter_from_home_duration);
- taskViewEnterFromHomeStaggerDelay =
- res.getInteger(R.integer.recents_task_enter_from_home_stagger_delay);
- taskViewExitToAppDuration =
- res.getInteger(R.integer.recents_task_exit_to_app_duration);
- taskViewExitToHomeDuration =
- res.getInteger(R.integer.recents_task_exit_to_home_duration);
- taskViewRemoveAnimDuration =
- res.getInteger(R.integer.recents_animate_task_view_remove_duration);
- taskViewRemoveAnimTranslationXPx =
- res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x);
- taskViewRoundedCornerRadiusPx =
- res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
- taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
- taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
- taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
- taskViewAffiliateGroupEnterOffsetPx =
- res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset);
- taskViewThumbnailAlpha = res.getFloat(R.dimen.recents_task_view_thumbnail_alpha);
-
- // Task bar colors
- taskBarViewDefaultBackgroundColor = context.getColor(
- R.color.recents_task_bar_default_background_color);
- taskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
- taskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
- taskBarViewHighlightColor = context.getColor(R.color.recents_task_bar_highlight_color);
- taskBarViewAffiliationColorMinAlpha = res.getFloat(
- R.dimen.recents_task_affiliation_color_min_alpha_percentage);
-
- // Task bar size & animations
- taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
- taskBarDismissDozeDelaySeconds =
- res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds);
-
- // Nav bar scrim
- navBarScrimEnterDuration =
- res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
-
- // Misc
- useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
- altTabKeyDelay = res.getInteger(R.integer.recents_alt_tab_key_delay);
- fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
- svelteLevel = res.getInteger(R.integer.recents_svelte_level);
- }
-
- /** Updates the system insets */
- public void updateSystemInsets(Rect insets) {
- systemInsets.set(insets);
- }
-
- /** Updates the states that need to be re-read whenever we re-initialize. */
- void updateOnReinitialize(Context context, SystemServicesProxy ssp) {
- // Check if the developer options are enabled
- developerOptionsEnabled = ssp.getGlobalSetting(context,
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
- lockToAppEnabled = ssp.getSystemSetting(context,
- Settings.System.LOCK_TO_APP_ENABLED) != 0;
- multiStackEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
+ /**
+ * Returns the activity launch state.
+ * TODO: This will be refactored out of RecentsConfiguration.
+ */
+ public RecentsActivityLaunchState getLaunchState() {
+ return mLaunchState;
}
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
- // Reset this flag on configuration change to ensure that we recreate new task views
- launchedReuseTaskStackViews = false;
- // Set this flag to indicate that the configuration has changed since Recents last launched
- launchedHasConfigurationChanged = true;
- }
-
- /** Returns whether the status bar scrim should be animated when shown for the first time. */
- public boolean shouldAnimateStatusBarScrim() {
- return launchedFromHome;
- }
-
- /** Returns whether the status bar scrim should be visible. */
- public boolean hasStatusBarScrim() {
- return !launchedWithNoRecentTasks;
- }
-
- /** Returns whether the nav bar scrim should be animated when shown for the first time. */
- public boolean shouldAnimateNavBarScrim() {
- return true;
- }
-
- /** Returns whether the nav bar scrim should be visible. */
- public boolean hasNavBarScrim() {
- // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
- return !launchedWithNoRecentTasks && (!hasTransposedNavBar || !isLandscape);
+ mLaunchState.updateOnConfigurationChange();
}
/**
* Returns the task stack bounds in the current orientation. These bounds do not account for
* the system insets.
*/
- public void getAvailableTaskStackBounds(int windowWidth, int windowHeight, int topInset,
+ public void getAvailableTaskStackBounds(Rect windowBounds, int topInset,
int rightInset, Rect searchBarBounds, Rect taskStackBounds) {
- if (isLandscape && hasTransposedSearchBar) {
- // In landscape, the search bar appears on the left, but we overlay it on top
- taskStackBounds.set(0, topInset, windowWidth - rightInset, windowHeight);
+ if (hasTransposedNavBar) {
+ // In landscape phones, the search bar appears on the left, but we overlay it on top
+ int swInset = getInsetToSmallestWidth(windowBounds.right - rightInset -
+ windowBounds.left);
+ taskStackBounds.set(windowBounds.left + swInset, windowBounds.top + topInset,
+ windowBounds.right - swInset - rightInset, windowBounds.bottom);
} else {
// In portrait, the search bar appears on the top (which already has the inset)
- taskStackBounds.set(0, searchBarBounds.bottom, windowWidth, windowHeight);
+ int swInset = getInsetToSmallestWidth(windowBounds.right - windowBounds.left);
+ taskStackBounds.set(windowBounds.left + swInset, searchBarBounds.bottom,
+ windowBounds.right - swInset, windowBounds.bottom);
}
}
@@ -335,16 +173,27 @@
* Returns the search bar bounds in the current orientation. These bounds do not account for
* the system insets.
*/
- public void getSearchBarBounds(int windowWidth, int windowHeight, int topInset,
- Rect searchBarSpaceBounds) {
+ public void getSearchBarBounds(Rect windowBounds, int topInset, Rect searchBarSpaceBounds) {
// Return empty rects if search is not enabled
int searchBarSize = searchBarSpaceHeightPx;
- if (isLandscape && hasTransposedSearchBar) {
- // In landscape, the search bar appears on the left
- searchBarSpaceBounds.set(0, topInset, searchBarSize, windowHeight);
+ if (hasTransposedSearchBar) {
+ // In landscape phones, the search bar appears on the left
+ searchBarSpaceBounds.set(windowBounds.left, windowBounds.top + topInset,
+ windowBounds.left + searchBarSize, windowBounds.bottom);
} else {
// In portrait, the search bar appears on the top
- searchBarSpaceBounds.set(0, topInset, windowWidth, topInset + searchBarSize);
+ searchBarSpaceBounds.set(windowBounds.left, windowBounds.top + topInset,
+ windowBounds.right, windowBounds.top + topInset + searchBarSize);
}
}
+
+ /**
+ * Constrain the width of the landscape stack to the smallest width of the device.
+ */
+ private int getInsetToSmallestWidth(int availableWidth) {
+ if (availableWidth > smallestWidth) {
+ return (availableWidth - smallestWidth) / 2;
+ }
+ return 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 300ea2a..59df293 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -16,27 +16,24 @@
package com.android.systemui.recents;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.FragmentManager;
-import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Rect;
import android.os.Bundle;
-import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
+import android.widget.Toast;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.views.RecentsView;
-import java.util.ArrayList;
-
/**
* A helper for the dialogs that show when task debugging is on.
*/
@@ -54,10 +51,18 @@
private static final int PLACE_BOTTOM_LEFT = 7;
private static final int PLACE_BOTTOM_RIGHT = 8;
private static final int PLACE_FULL = 9;
+ private static final int PLACE_DOCK_LEFT = 10;
+ private static final int PLACE_DOCK_RIGHT = 11;
+ private static final int PLACE_DOCK_TOP = 12;
+ private static final int PLACE_DOCK_BOTTOM = 13;
// The button resource ID combined with the arrangement command.
private static final int[][] BUTTON_DEFINITIONS =
- {{R.id.place_left, PLACE_LEFT},
+ {{R.id.place_dock_left, PLACE_DOCK_LEFT},
+ {R.id.place_dock_right, PLACE_DOCK_RIGHT},
+ {R.id.place_dock_top, PLACE_DOCK_TOP},
+ {R.id.place_dock_bottom, PLACE_DOCK_BOTTOM},
+ {R.id.place_left, PLACE_LEFT},
{R.id.place_right, PLACE_RIGHT},
{R.id.place_top, PLACE_TOP},
{R.id.place_bottom, PLACE_BOTTOM},
@@ -76,6 +81,12 @@
private Rect[] mBounds = {new Rect(), new Rect(), new Rect(), new Rect()};
private Task[] mTasks = {null, null, null, null};
+ /**
+ * Called by FragmentManager
+ */
+ public RecentsResizeTaskDialog() {
+ }
+
public RecentsResizeTaskDialog(FragmentManager mgr, RecentsActivity activity) {
mFragmentManager = mgr;
mRecentsActivity = activity;
@@ -86,13 +97,11 @@
void showResizeTaskDialog(Task mainTask, RecentsView rv) {
mTasks[0] = mainTask;
mRecentsView = rv;
-
- show(mFragmentManager, TAG);
+ showAllowingStateLoss(mFragmentManager, TAG);
}
/** Creates a new resize-task dialog. */
- private void createResizeTaskDialog(final Context context, LayoutInflater inflater,
- AlertDialog.Builder builder) {
+ private void createResizeTaskDialog(LayoutInflater inflater, AlertDialog.Builder builder) {
builder.setTitle(R.string.recents_caption_resize);
mResizeTaskDialogContent =
inflater.inflate(R.layout.recents_task_resize_dialog, null, false);
@@ -104,7 +113,17 @@
b.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v) {
- placeTasks(action);
+ switch (action) {
+ case PLACE_DOCK_LEFT:
+ case PLACE_DOCK_RIGHT:
+ case PLACE_DOCK_TOP:
+ case PLACE_DOCK_BOTTOM:
+ placeDockTasks(action);
+ break;
+ default:
+ placeTasks(action);
+ break;
+ }
}
});
}
@@ -113,7 +132,7 @@
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- dismiss();
+ dismissAllowingStateLoss();
}
});
@@ -122,7 +141,7 @@
/** Helper function to place window(s) on the display according to an arrangement request. */
private void placeTasks(int arrangement) {
- Rect rect = mSsp.getWindowRect();
+ Rect rect = mSsp.getDisplayRect();
for (int i = 0; i < mBounds.length; ++i) {
mBounds[i].set(rect);
if (i != 0) {
@@ -197,7 +216,7 @@
break;
case PLACE_FULL:
// Nothing to change.
- mBounds[0] = null;
+ mBounds[0] = new Rect();
break;
}
@@ -211,12 +230,12 @@
}
// Get rid of the dialog.
- dismiss();
+ dismissAllowingStateLoss();
mRecentsActivity.dismissRecentsToHomeWithoutTransitionAnimation();
// In debug mode, we force all task to be resizeable regardless of the
// current app configuration.
- if (RecentsConfiguration.getInstance().multiStackEnabled) {
+ if (RecentsConfiguration.getInstance().multiWindowEnabled) {
for (int i = additionalTasks; i >= 0; --i) {
if (mTasks[i] != null) {
mSsp.setTaskResizeable(mTasks[i].key.id);
@@ -233,12 +252,44 @@
}
}
+ /**
+ * Helper function to place docked window(s) on the display according to an arrangement request.
+ */
+ private void placeDockTasks(int arrangement) {
+ int createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ switch (arrangement) {
+ case PLACE_DOCK_LEFT:
+ createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ break;
+ case PLACE_DOCK_TOP:
+ createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ break;
+ case PLACE_DOCK_RIGHT:
+ createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ break;
+ case PLACE_DOCK_BOTTOM:
+ createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ break;
+ }
+
+ // Dismiss the dialog before trying to launch the task
+ dismissAllowingStateLoss();
+
+ if (mTasks[0].key.stackId != ActivityManager.DOCKED_STACK_ID) {
+ int taskId = mTasks[0].key.id;
+ mSsp.setTaskResizeable(taskId);
+ mSsp.dockTask(taskId, createMode);
+ mRecentsView.launchTask(mTasks[0], null);
+ } else {
+ Toast.makeText(getContext(), "Already docked", Toast.LENGTH_SHORT);
+ }
+ }
+
@Override
public Dialog onCreateDialog(Bundle args) {
- final Context context = this.getActivity();
LayoutInflater inflater = getActivity().getLayoutInflater();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- createResizeTaskDialog(context, inflater, builder);
+ createResizeTaskDialog(inflater, builder);
return builder.create();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index cbf5c05..231843e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -39,7 +39,6 @@
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.systemui.R;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
deleted file mode 100644
index fbf8a86..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.misc;
-
-import android.os.Handler;
-import android.os.SystemClock;
-import android.view.KeyEvent;
-import com.android.systemui.recents.Constants;
-
-/**
- * A trigger for catching a debug chord.
- * We currently use volume up then volume down to trigger this mode.
- */
-public class DebugTrigger {
-
- Handler mHandler;
- Runnable mTriggeredRunnable;
-
- int mLastKeyCode;
- long mLastKeyCodeTime;
-
- public DebugTrigger(Runnable triggeredRunnable) {
- mHandler = new Handler();
- mTriggeredRunnable = triggeredRunnable;
- }
-
- /** Resets the debug trigger */
- void reset() {
- mLastKeyCode = 0;
- mLastKeyCodeTime = 0;
- }
-
- /**
- * Processes a key event and tests if it is a part of the trigger. If the chord is complete,
- * then we just call the callback.
- */
- public void onKeyEvent(int keyCode) {
- if (!Constants.DebugFlags.App.EnableDebugMode) return;
-
- if (mLastKeyCode == 0) {
- if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
- mLastKeyCode = keyCode;
- mLastKeyCodeTime = SystemClock.uptimeMillis();
- return;
- }
- } else {
- if (mLastKeyCode == KeyEvent.KEYCODE_VOLUME_UP &&
- keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
- if ((SystemClock.uptimeMillis() - mLastKeyCodeTime) < 750) {
- mTriggeredRunnable.run();
- }
- }
- }
- reset();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index bead1b0..b6d25f5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -55,18 +55,18 @@
import android.provider.Settings;
import android.util.Log;
import android.util.MutableBoolean;
+import android.util.MutableFloat;
+import android.util.MutableInt;
import android.util.Pair;
-import android.util.SparseArray;
+import android.util.Size;
import android.view.Display;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-
import com.android.internal.app.AssistUtils;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsConfiguration;
import java.io.IOException;
import java.util.ArrayList;
@@ -282,6 +282,30 @@
}
}
+ /**
+ * Resizes the given task to the new bounds.
+ */
+ public void resizeTask(int taskId, Rect bounds) {
+ if (mIam == null) return;
+
+ try {
+ mIam.resizeTask(taskId, bounds, ActivityManager.RESIZE_MODE_FORCED);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /** Docks a task to the side of the screen. */
+ public void dockTask(int taskId, int createMode) {
+ if (mIam == null) return;
+
+ try {
+ mIam.moveTaskToDockedStack(taskId, createMode, true);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Returns the focused stack id. */
public int getFocusedStack() {
if (mIam == null) return -1;
@@ -637,16 +661,48 @@
}
/**
- * Returns the window rect.
+ * Returns the smallest width/height.
*/
- public Rect getWindowRect() {
- Rect windowRect = new Rect();
- if (mWm == null) return windowRect;
+ public int getDeviceSmallestWidth() {
+ if (mWm == null) return 0;
+
+ Point smallestSizeRange = new Point();
+ Point largestSizeRange = new Point();
+ mWm.getDefaultDisplay().getCurrentSizeRange(smallestSizeRange, largestSizeRange);
+ return smallestSizeRange.x;
+ }
+
+ /**
+ * Returns the display rect.
+ */
+ public Rect getDisplayRect() {
+ Rect displayRect = new Rect();
+ if (mWm == null) return displayRect;
Point p = new Point();
mWm.getDefaultDisplay().getRealSize(p);
- windowRect.set(0, 0, p.x, p.y);
- return windowRect;
+ displayRect.set(0, 0, p.x, p.y);
+ return displayRect;
+ }
+
+ /**
+ * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
+ */
+ public Rect getWindowRect() {
+ Rect windowRect = new Rect();
+ if (mIam == null) return windowRect;
+
+ try {
+ // Use the home stack bounds
+ ActivityManager.StackInfo stackInfo = mIam.getStackInfo(ActivityManager.HOME_STACK_ID);
+ if (stackInfo != null) {
+ windowRect.set(stackInfo.bounds);
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } finally {
+ return windowRect;
+ }
}
/** Starts an activity from recents. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index b8015c0..6ef7253 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -20,12 +20,9 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Log;
-import android.util.SparseArray;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -63,7 +60,7 @@
SystemServicesProxy mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> mRawTasks;
- SparseArray<TaskStack> mStacks = new SparseArray<>();
+ TaskStack mStack;
HashMap<Task.ComponentNameKey, ActivityInfoHandle> mActivityInfoCache =
new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
@@ -78,7 +75,7 @@
* An optimization to preload the raw list of tasks.
*/
public synchronized void preloadRawTasks(boolean isTopTaskHome) {
- mRawTasks = mSystemServicesProxy.getRecentTasks(mConfig.maxNumTasksToLoad,
+ mRawTasks = mSystemServicesProxy.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
Collections.reverse(mRawTasks);
@@ -96,11 +93,8 @@
// This activity info cache will be used for both preloadPlan() and executePlan()
mActivityInfoCache.clear();
- // TODO (multi-display): Currently assume the primary display
- Rect displayBounds = mSystemServicesProxy.getWindowRect();
-
Resources res = mContext.getResources();
- SparseArray<ArrayList<Task>> stacksTasks = new SparseArray<>();
+ ArrayList<Task> stackTasks = new ArrayList<>();
if (mRawTasks == null) {
preloadRawTasks(isTopTaskHome);
}
@@ -130,7 +124,7 @@
activityLabel, mSystemServicesProxy, res);
Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
mSystemServicesProxy, res, infoHandle, false);
- int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig);
+ int activityColor = loader.getActivityPrimaryColor(t.taskDescription, res);
// Update the activity info cache
if (!hadCachedActivityInfo && infoHandle.info != null) {
@@ -152,37 +146,13 @@
task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
- if (!mConfig.multiStackEnabled ||
- Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
- int firstStackId = 0;
- ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);
- if (stackTasks == null) {
- stackTasks = new ArrayList<>();
- stacksTasks.put(firstStackId, stackTasks);
- }
- stackTasks.add(task);
- } else {
- ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);
- if (stackTasks == null) {
- stackTasks = new ArrayList<>();
- stacksTasks.put(t.stackId, stackTasks);
- }
- stackTasks.add(task);
- }
+ stackTasks.add(task);
}
// Initialize the stacks
- mStacks.clear();
- int stackCount = stacksTasks.size();
- for (int i = 0; i < stackCount; i++) {
- int stackId = stacksTasks.keyAt(i);
- ArrayList<Task> stackTasks = stacksTasks.valueAt(i);
- TaskStack stack = new TaskStack(stackId);
- stack.setBounds(displayBounds, displayBounds);
- stack.setTasks(stackTasks);
- stack.createAffiliatedGroupings(mConfig);
- mStacks.put(stackId, stack);
- }
+ mStack = new TaskStack();
+ mStack.setTasks(stackTasks);
+ mStack.createAffiliatedGroupings(mContext);
}
/**
@@ -197,92 +167,70 @@
Resources res = mContext.getResources();
// Iterate through each of the tasks and load them according to the load conditions.
- int stackCount = mStacks.size();
- for (int j = 0; j < stackCount; j++) {
- ArrayList<Task> tasks = mStacks.valueAt(j).getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
- Task task = tasks.get(i);
- Task.TaskKey taskKey = task.key;
+ ArrayList<Task> tasks = mStack.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+ Task task = tasks.get(i);
+ Task.TaskKey taskKey = task.key;
- // Get an existing activity info handle if possible
- Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
- ActivityInfoHandle infoHandle;
- boolean hadCachedActivityInfo = false;
- if (mActivityInfoCache.containsKey(cnKey)) {
- infoHandle = mActivityInfoCache.get(cnKey);
- hadCachedActivityInfo = true;
- } else {
- infoHandle = new ActivityInfoHandle();
+ // Get an existing activity info handle if possible
+ Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
+ ActivityInfoHandle infoHandle;
+ boolean hadCachedActivityInfo = false;
+ if (mActivityInfoCache.containsKey(cnKey)) {
+ infoHandle = mActivityInfoCache.get(cnKey);
+ hadCachedActivityInfo = true;
+ } else {
+ infoHandle = new ActivityInfoHandle();
+ }
+
+ boolean isRunningTask = (task.key.id == opts.runningTaskId);
+ boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
+ boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
+
+ // If requested, skip the running task
+ if (opts.onlyLoadPausedActivities && isRunningTask) {
+ continue;
+ }
+
+ if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
+ if (task.activityIcon == null) {
+ if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
+ task.activityIcon = loader.getAndUpdateActivityIcon(taskKey,
+ t.taskDescription, mSystemServicesProxy, res, infoHandle, true);
}
-
- boolean isRunningTask = (task.key.id == opts.runningTaskId);
- boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
- boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
-
- // If requested, skip the running task
- if (opts.onlyLoadPausedActivities && isRunningTask) {
- continue;
- }
-
- if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
- if (task.activityIcon == null) {
- if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
- task.activityIcon = loader.getAndUpdateActivityIcon(taskKey,
- t.taskDescription, mSystemServicesProxy, res, infoHandle, true);
+ }
+ if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
+ if (task.thumbnail == null || isRunningTask) {
+ if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
+ if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
+ task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
+ mSystemServicesProxy, true);
+ } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
+ loadQueue.addTask(task);
}
}
- if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
- if (task.thumbnail == null || isRunningTask) {
- if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
- if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
- mSystemServicesProxy, true);
- } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
- loadQueue.addTask(task);
- }
- }
- }
+ }
- // Update the activity info cache
- if (!hadCachedActivityInfo && infoHandle.info != null) {
- mActivityInfoCache.put(cnKey, infoHandle);
- }
+ // Update the activity info cache
+ if (!hadCachedActivityInfo && infoHandle.info != null) {
+ mActivityInfoCache.put(cnKey, infoHandle);
}
}
}
/**
- * Returns all TaskStacks from the preloaded list of recent tasks.
+ * Returns the TaskStack from the preloaded list of recent tasks.
*/
- public ArrayList<TaskStack> getAllTaskStacks() {
- ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
- int stackCount = mStacks.size();
- for (int i = 0; i < stackCount; i++) {
- stacks.add(mStacks.valueAt(i));
- }
- // Ensure that we have at least one stack
- if (stacks.isEmpty()) {
- stacks.add(new TaskStack());
- }
- return stacks;
- }
-
- /**
- * Returns a specific TaskStack from the preloaded list of recent tasks.
- */
- public TaskStack getTaskStack(int stackId) {
- return mStacks.get(stackId);
+ public TaskStack getTaskStack() {
+ return mStack;
}
/** Returns whether there are any tasks in any stacks. */
public boolean hasTasks() {
- int stackCount = mStacks.size();
- for (int i = 0; i < stackCount; i++) {
- if (mStacks.valueAt(i).getTaskCount() > 0) {
- return true;
- }
+ if (mStack != null) {
+ return mStack.getTaskCount() > 0;
}
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index ad25c85..760382e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -27,7 +27,6 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
-
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
@@ -248,7 +247,7 @@
}
/* Recents task loader
- * NOTE: We should not hold any references to a Context from a static instance */
+ * NOTE: We should not hold any references to non-application Context from a static instance */
public class RecentsTaskLoader {
private static final String TAG = "RecentsTaskLoader";
@@ -438,12 +437,11 @@
}
/** Returns the activity's primary color. */
- public int getActivityPrimaryColor(ActivityManager.TaskDescription td,
- RecentsConfiguration config) {
+ public int getActivityPrimaryColor(ActivityManager.TaskDescription td, Resources res) {
if (td != null && td.getPrimaryColor() != 0) {
return td.getPrimaryColor();
}
- return config.taskBarViewDefaultBackgroundColor;
+ return res.getColor(R.color.recents_task_bar_default_background_color);
}
/** Returns the size of the app icon cache. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 5aaea15..20d9203 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,12 +16,12 @@
package com.android.systemui.recents.model;
+import android.content.Context;
import android.graphics.Color;
-import android.graphics.Rect;
import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.NamedCounter;
import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.R;
import java.util.ArrayList;
import java.util.Collections;
@@ -177,35 +177,17 @@
// The task offset to apply to a task id as a group affiliation
static final int IndividualTaskIdOffset = 1 << 16;
- public final int id;
- public final Rect stackBounds = new Rect();
- public final Rect displayBounds = new Rect();
-
FilteredTaskList mTaskList = new FilteredTaskList();
TaskStackCallbacks mCb;
ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<Integer, TaskGrouping>();
- public TaskStack() {
- this(0);
- }
-
- public TaskStack(int stackId) {
- id = stackId;
- }
-
/** Sets the callbacks for this task stack. */
public void setCallbacks(TaskStackCallbacks cb) {
mCb = cb;
}
- /** Sets the bounds of this stack. */
- public void setBounds(Rect stackBounds, Rect displayBounds) {
- this.stackBounds.set(stackBounds);
- this.displayBounds.set(displayBounds);
- }
-
/** Resets this TaskStack. */
public void reset() {
mCb = null;
@@ -386,7 +368,7 @@
/**
* Temporary: This method will simulate affiliation groups by
*/
- public void createAffiliatedGroupings(RecentsConfiguration config) {
+ public void createAffiliatedGroupings(Context context) {
if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
// Sort all tasks by increasing firstActiveTime of the task
@@ -471,7 +453,8 @@
tasksMap.put(t.key, t);
}
// Update the task colors for each of the groups
- float minAlpha = config.taskBarViewAffiliationColorMinAlpha;
+ float minAlpha = context.getResources().getFloat(
+ R.dimen.recents_task_affiliation_color_min_alpha_percentage);
int taskGroupCount = mGroups.size();
for (int i = 0; i < taskGroupCount; i++) {
TaskGrouping group = mGroups.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java
deleted file mode 100644
index 452830d..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Pair;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.SeekBar;
-import com.android.systemui.R;
-import com.android.systemui.recents.RecentsConfiguration;
-
-import java.util.ArrayList;
-
-/**
- * A full screen overlay layer that allows us to draw views from throughout the system on the top
- * most layer.
- */
-public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarChangeListener {
-
- public interface DebugOverlayViewCallbacks {
- public void onPrimarySeekBarChanged(float progress);
- public void onSecondarySeekBarChanged(float progress);
- }
-
- final static int sCornerRectSize = 50;
-
- RecentsConfiguration mConfig;
- DebugOverlayViewCallbacks mCb;
-
- ArrayList<Pair<Rect, Integer>> mRects = new ArrayList<Pair<Rect, Integer>>();
- String mText;
- Paint mDebugOutline = new Paint();
- Paint mTmpPaint = new Paint();
- Rect mTmpRect = new Rect();
- boolean mEnabled = true;
-
- SeekBar mPrimarySeekBar;
- SeekBar mSecondarySeekBar;
-
- public DebugOverlayView(Context context) {
- this(context, null);
- }
-
- public DebugOverlayView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public DebugOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public DebugOverlayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mConfig = RecentsConfiguration.getInstance();
- mDebugOutline.setColor(0xFFff0000);
- mDebugOutline.setStyle(Paint.Style.STROKE);
- mDebugOutline.setStrokeWidth(8f);
- setWillNotDraw(false);
- }
-
- public void setCallbacks(DebugOverlayViewCallbacks cb) {
- mCb = cb;
- }
-
- @Override
- protected void onFinishInflate() {
- mPrimarySeekBar = (SeekBar) findViewById(R.id.debug_seek_bar_1);
- mPrimarySeekBar.setOnSeekBarChangeListener(this);
- mSecondarySeekBar = (SeekBar) findViewById(R.id.debug_seek_bar_2);
- mSecondarySeekBar.setOnSeekBarChangeListener(this);
- }
-
- /** Enables the debug overlay drawing. */
- public void enable() {
- mEnabled = true;
- setVisibility(View.VISIBLE);
- }
-
- /** Disables the debug overlay drawing. */
- public void disable() {
- mEnabled = false;
- setVisibility(View.GONE);
- }
-
- /** Clears all debug rects. */
- public void clear() {
- mRects.clear();
- }
-
- /** Adds a rect to be drawn. */
- void addRect(Rect r, int color) {
- mRects.add(new Pair<Rect, Integer>(r, color));
- invalidate();
- }
-
- /** Adds a view's global rect to be drawn. */
- void addViewRect(View v, int color) {
- Rect vr = new Rect();
- v.getGlobalVisibleRect(vr);
- mRects.add(new Pair<Rect, Integer>(vr, color));
- invalidate();
- }
-
- /** Adds a rect, relative to a given view to be drawn. */
- void addRectRelativeToView(View v, Rect r, int color) {
- Rect vr = new Rect();
- v.getGlobalVisibleRect(vr);
- r.offsetTo(vr.left, vr.top);
- mRects.add(new Pair<Rect, Integer>(r, color));
- invalidate();
- }
-
- /** Sets the debug text at the bottom of the screen. */
- void setText(String message) {
- mText = message;
- invalidate();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- addRect(new Rect(0, 0, sCornerRectSize, sCornerRectSize), 0xFFff0000);
- addRect(new Rect(getMeasuredWidth() - sCornerRectSize, getMeasuredHeight() - sCornerRectSize,
- getMeasuredWidth(), getMeasuredHeight()), 0xFFff0000);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (mEnabled) {
- // Draw the outline
- canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mDebugOutline);
-
- // Draw the rects
- int numRects = mRects.size();
- for (int i = 0; i < numRects; i++) {
- Pair<Rect, Integer> r = mRects.get(i);
- mTmpPaint.setColor(r.second);
- canvas.drawRect(r.first, mTmpPaint);
- }
-
- // Draw the text
- if (mText != null && mText.length() > 0) {
- mTmpPaint.setColor(0xFFff0000);
- mTmpPaint.setTextSize(60);
- mTmpPaint.getTextBounds(mText, 0, 1, mTmpRect);
- canvas.drawText(mText, 10f, getMeasuredHeight() - mTmpRect.height() - mConfig.systemInsets.bottom, mTmpPaint);
- }
- }
- }
-
- /**** SeekBar.OnSeekBarChangeListener Implementation ****/
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- // Do nothing
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- // Do nothing
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (seekBar == mPrimarySeekBar) {
- mCb.onPrimarySeekBarChanged((float) progress / mPrimarySeekBar.getMax());
- } else if (seekBar == mSecondarySeekBar) {
- mCb.onSecondarySeekBarChanged((float) progress / mSecondarySeekBar.getMax());
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
index 509ad1b..682fd8f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
@@ -28,7 +28,6 @@
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.util.Log;
-
import com.android.systemui.R;
import com.android.systemui.recents.RecentsConfiguration;
@@ -90,7 +89,8 @@
mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mCornerShadowPaint.setStyle(Paint.Style.FILL);
mCornerShadowPaint.setDither(true);
- mCornerRadius = config.taskViewRoundedCornerRadiusPx;
+ mCornerRadius = resources.getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius);
mCardBounds = new RectF();
mEdgeShadowPaint = new Paint(mCornerShadowPaint);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 651b29a..92ed0f1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -38,8 +38,9 @@
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManagerGlobal;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
@@ -78,13 +79,14 @@
RecentsConfiguration mConfig;
LayoutInflater mInflater;
- DebugOverlayView mDebugOverlay;
- RecentsViewLayoutAlgorithm mLayoutAlgorithm;
ArrayList<TaskStack> mStacks;
- List<TaskStackView> mTaskStackViews = new ArrayList<>();
+ TaskStackView mTaskStackView;
RecentsAppWidgetHostView mSearchBar;
RecentsViewCallbacks mCb;
+ Interpolator mFastOutSlowInInterpolator;
+
+ Rect mSystemInsets = new Rect();
public RecentsView(Context context) {
super(context);
@@ -102,7 +104,8 @@
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
- mLayoutAlgorithm = new RecentsViewLayoutAlgorithm(mConfig);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
}
/** Sets the callbacks */
@@ -110,67 +113,37 @@
mCb = cb;
}
- /** Sets the debug overlay */
- public void setDebugOverlay(DebugOverlayView overlay) {
- mDebugOverlay = overlay;
- }
-
/** Set/get the bsp root node */
- public void setTaskStacks(ArrayList<TaskStack> stacks) {
- int numStacks = stacks.size();
-
- // Remove all/extra stack views
- int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout
- if (mConfig.launchedReuseTaskStackViews) {
- numTaskStacksToKeep = Math.min(mTaskStackViews.size(), numStacks);
- }
- for (int i = mTaskStackViews.size() - 1; i >= numTaskStacksToKeep; i--) {
- removeView(mTaskStackViews.remove(i));
- }
-
- // Update the stack views that we are keeping
- for (int i = 0; i < numTaskStacksToKeep; i++) {
- TaskStackView tsv = mTaskStackViews.get(i);
- // If onRecentsHidden is not triggered, we need to the stack view again here
- tsv.reset();
- tsv.setStack(stacks.get(i));
- }
-
- // Add remaining/recreate stack views
- mStacks = stacks;
- for (int i = mTaskStackViews.size(); i < numStacks; i++) {
- TaskStack stack = stacks.get(i);
- TaskStackView stackView = new TaskStackView(getContext(), stack);
- stackView.setCallbacks(this);
- addView(stackView);
- mTaskStackViews.add(stackView);
- }
-
- // Enable debug mode drawing on all the stacks if necessary
- if (mConfig.debugModeEnabled) {
- for (int i = mTaskStackViews.size() - 1; i >= 0; i--) {
- TaskStackView stackView = mTaskStackViews.get(i);
- stackView.setDebugOverlay(mDebugOverlay);
+ public void setTaskStack(TaskStack stack) {
+ if (mConfig.getLaunchState().launchedReuseTaskStackViews) {
+ if (mTaskStackView != null) {
+ // If onRecentsHidden is not triggered, we need to the stack view again here
+ mTaskStackView.reset();
+ mTaskStackView.setStack(stack);
+ } else {
+ mTaskStackView = new TaskStackView(getContext(), stack);
+ mTaskStackView.setCallbacks(this);
+ addView(mTaskStackView);
}
+ } else {
+ if (mTaskStackView != null) {
+ removeView(mTaskStackView);
+ }
+ mTaskStackView = new TaskStackView(getContext(), stack);
+ mTaskStackView.setCallbacks(this);
+ addView(mTaskStackView);
}
// Trigger a new layout
requestLayout();
}
- /** Gets the list of task views */
- List<TaskStackView> getTaskStackViews() {
- return mTaskStackViews;
- }
-
/** Gets the next task in the stack - or if the last - the top task */
public Task getNextTaskOrTopTask(Task taskToSearch) {
Task returnTask = null;
boolean found = false;
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = stackCount - 1; i >= 0; --i) {
- TaskStack stack = stackViews.get(i).getStack();
+ if (mTaskStackView != null) {
+ TaskStack stack = mTaskStackView.getStack();
ArrayList<Task> taskList = stack.getTasks();
// Iterate the stack views and try and find the focused task
for (int j = taskList.size() - 1; j >= 0; --j) {
@@ -190,20 +163,16 @@
/** Launches the focused task from the first stack if possible */
public boolean launchFocusedTask() {
- // Get the first stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- TaskStack stack = stackView.getStack();
+ if (mTaskStackView != null) {
+ TaskStack stack = mTaskStackView.getStack();
// Iterate the stack views and try and find the focused task
- List<TaskView> taskViews = stackView.getTaskViews();
+ List<TaskView> taskViews = mTaskStackView.getTaskViews();
int taskViewCount = taskViews.size();
for (int j = 0; j < taskViewCount; j++) {
TaskView tv = taskViews.get(j);
Task task = tv.getTask();
if (tv.isFocusedTask()) {
- onTaskViewClicked(stackView, tv, stack, task, false, false, null);
+ onTaskViewClicked(mTaskStackView, tv, stack, task, false, false, null);
return true;
}
}
@@ -213,19 +182,16 @@
/** Launches a given task. */
public boolean launchTask(Task task, Rect taskBounds) {
- // Get the first stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- TaskStack stack = stackView.getStack();
+ if (mTaskStackView != null) {
+ TaskStack stack = mTaskStackView.getStack();
// Iterate the stack views and try and find the given task.
- List<TaskView> taskViews = stackView.getTaskViews();
+ List<TaskView> taskViews = mTaskStackView.getTaskViews();
int taskViewCount = taskViews.size();
for (int j = 0; j < taskViewCount; j++) {
TaskView tv = taskViews.get(j);
if (tv.getTask() == task) {
- onTaskViewClicked(stackView, tv, stack, task, false, true, taskBounds);
+ onTaskViewClicked(mTaskStackView, tv, stack, task, false, taskBounds != null,
+ taskBounds);
return true;
}
}
@@ -235,12 +201,8 @@
/** Launches the task that Recents was launched from, if possible */
public boolean launchPreviousTask() {
- // Get the first stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- TaskStack stack = stackView.getStack();
+ if (mTaskStackView != null) {
+ TaskStack stack = mTaskStackView.getStack();
ArrayList<Task> tasks = stack.getTasks();
// Find the launch task in the stack
@@ -249,8 +211,8 @@
for (int j = 0; j < taskCount; j++) {
if (tasks.get(j).isLaunchTarget) {
Task task = tasks.get(j);
- TaskView tv = stackView.getChildViewForTask(task);
- onTaskViewClicked(stackView, tv, stack, task, false, false, null);
+ TaskView tv = mTaskStackView.getChildViewForTask(task);
+ onTaskViewClicked(mTaskStackView, tv, stack, task, false, false, null);
return true;
}
}
@@ -264,12 +226,8 @@
// We have to increment/decrement the post animation trigger in case there are no children
// to ensure that it runs
ctx.postAnimationTrigger.increment();
-
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- stackView.startEnterRecentsAnimation(ctx);
+ if (mTaskStackView != null) {
+ mTaskStackView.startEnterRecentsAnimation(ctx);
}
ctx.postAnimationTrigger.decrement();
}
@@ -279,11 +237,8 @@
// We have to increment/decrement the post animation trigger in case there are no children
// to ensure that it runs
ctx.postAnimationTrigger.increment();
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- stackView.startExitToHomeAnimation(ctx);
+ if (mTaskStackView != null) {
+ mTaskStackView.startExitToHomeAnimation(ctx);
}
ctx.postAnimationTrigger.decrement();
@@ -329,31 +284,19 @@
// Get the search bar bounds and measure the search bar layout
Rect searchBarSpaceBounds = new Rect();
if (mSearchBar != null) {
- mConfig.getSearchBarBounds(width, height, mConfig.systemInsets.top, searchBarSpaceBounds);
+ mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+ searchBarSpaceBounds);
mSearchBar.measure(
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
}
Rect taskStackBounds = new Rect();
- mConfig.getAvailableTaskStackBounds(width, height, mConfig.systemInsets.top,
- mConfig.systemInsets.right, searchBarSpaceBounds, taskStackBounds);
-
- // Measure each TaskStackView with the full width and height of the window since the
- // transition view is a child of that stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- List<Rect> stackViewsBounds = mLayoutAlgorithm.computeStackRects(stackViews,
- taskStackBounds);
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- if (stackView.getVisibility() != GONE) {
- // We are going to measure the TaskStackView with the whole RecentsView dimensions,
- // but the actual stack is going to be inset to the bounds calculated by the layout
- // algorithm
- stackView.setStackInsetRect(stackViewsBounds.get(i));
- stackView.measure(widthMeasureSpec, heightMeasureSpec);
- }
+ mConfig.getAvailableTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+ mSystemInsets.right, searchBarSpaceBounds, taskStackBounds);
+ if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
+ mTaskStackView.setTaskStackBounds(taskStackBounds);
+ mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
}
setMeasuredDimension(width, height);
@@ -365,31 +308,23 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// Get the search bar bounds so that we lay it out
+ Rect measuredRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ Rect searchBarSpaceBounds = new Rect();
if (mSearchBar != null) {
- Rect searchBarSpaceBounds = new Rect();
- mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
- mConfig.systemInsets.top, searchBarSpaceBounds);
+ mConfig.getSearchBarBounds(measuredRect,
+ mSystemInsets.top, searchBarSpaceBounds);
mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
}
- // Layout each TaskStackView with the full width and height of the window since the
- // transition view is a child of that stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- if (stackView.getVisibility() != GONE) {
- stackView.layout(left, top, left + stackView.getMeasuredWidth(),
- top + stackView.getMeasuredHeight());
- }
+ if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
+ mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
}
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- // Update the configuration with the latest system insets and trigger a relayout
- mConfig.updateSystemInsets(insets.getSystemWindowInsets());
+ mSystemInsets.set(insets.getSystemWindowInsets());
requestLayout();
return insets.consumeSystemWindowInsets();
}
@@ -397,29 +332,24 @@
/** Notifies each task view of the user interaction. */
public void onUserInteraction() {
// Get the first stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- stackView.onUserInteraction();
+ if (mTaskStackView != null) {
+ mTaskStackView.onUserInteraction();
}
}
/** Focuses the next task in the first stack view */
public void focusNextTask(boolean forward) {
// Get the first stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- if (!stackViews.isEmpty()) {
- stackViews.get(0).focusNextTask(forward, true);
+ if (mTaskStackView != null) {
+ mTaskStackView.focusNextTask(forward, true);
}
}
/** Dismisses the focused task. */
public void dismissFocusedTask() {
// Get the first stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- if (!stackViews.isEmpty()) {
- stackViews.get(0).dismissFocusedTask();
+ if (mTaskStackView != null) {
+ mTaskStackView.dismissFocusedTask();
}
}
@@ -442,9 +372,8 @@
}
public void disableLayersForOneFrame() {
- List<TaskStackView> stackViews = getTaskStackViews();
- for (int i = 0; i < stackViews.size(); i++) {
- stackViews.get(i).disableLayersForOneFrame();
+ if (mTaskStackView != null) {
+ mTaskStackView.disableLayersForOneFrame();
}
}
@@ -624,7 +553,7 @@
// outside the display rect (to ensure we don't animate from too far away)
sourceView = stackView;
offsetX = transform.rect.left;
- offsetY = mConfig.displayRect.height();
+ offsetY = getMeasuredHeight();
} else {
sourceView = tv.mThumbnailView;
}
@@ -655,7 +584,7 @@
}
postDrawHeaderThumbnailTransitionRunnable(stackView, tv, offsetX, offsetY, stackScroll,
animStartedListener);
- if (mConfig.multiStackEnabled) {
+ if (mConfig.multiWindowEnabled) {
opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
R.anim.recents_from_unknown_enter,
R.anim.recents_from_unknown_exit,
@@ -670,7 +599,7 @@
opts = ActivityOptions.makeBasic();
}
if (boundsValid) {
- opts.setBounds(bounds);
+ opts.setBounds(bounds.isEmpty() ? null : bounds);
}
final ActivityOptions launchOpts = opts;
final boolean screenPinningRequested = (animStartedListener == null) && lockToTask;
@@ -767,11 +696,8 @@
/** Final callback after Recents is finally hidden. */
public void onRecentsHidden() {
// Notify each task stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- stackView.onRecentsHidden();
+ if (mTaskStackView != null) {
+ mTaskStackView.onRecentsHidden();
}
}
@@ -779,11 +705,13 @@
public void onTaskStackFilterTriggered() {
// Hide the search bar
if (mSearchBar != null) {
+ int filterDuration = getResources().getInteger(
+ R.integer.recents_filter_animate_current_views_duration);
mSearchBar.animate()
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.filteringCurrentViewsAnimDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(filterDuration)
.withLayer()
.start();
}
@@ -793,11 +721,13 @@
public void onTaskStackUnfilterTriggered() {
// Show the search bar
if (mSearchBar != null) {
+ int filterDuration = getResources().getInteger(
+ R.integer.recents_filter_animate_new_views_duration);
mSearchBar.animate()
.alpha(1f)
.setStartDelay(0)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.filteringNewViewsAnimDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(filterDuration)
.withLayer()
.start();
}
@@ -815,11 +745,8 @@
@Override
public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
// Propagate this event down to each task stack view
- List<TaskStackView> stackViews = getTaskStackViews();
- int stackCount = stackViews.size();
- for (int i = 0; i < stackCount; i++) {
- TaskStackView stackView = stackViews.get(i);
- stackView.onPackagesChanged(monitor, packageName, userId);
+ if (mTaskStackView != null) {
+ mTaskStackView.onPackagesChanged(monitor, packageName, userId);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
deleted file mode 100644
index eea273c..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import android.graphics.Rect;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.model.TaskStack;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/* The layout logic for the RecentsView. */
-public class RecentsViewLayoutAlgorithm {
-
- RecentsConfiguration mConfig;
-
- public RecentsViewLayoutAlgorithm(RecentsConfiguration config) {
- mConfig = config;
- }
-
- /** Return the relative coordinate given coordinates in another size. */
- private int getRelativeCoordinate(int availableOffset, int availableSize, int otherCoord, int otherSize) {
- float relPos = (float) otherCoord / otherSize;
- return availableOffset + (int) (relPos * availableSize);
- }
-
- /**
- * Computes and returns the bounds that each of the stack views should take up.
- */
- List<Rect> computeStackRects(List<TaskStackView> stackViews, Rect availableBounds) {
- ArrayList<Rect> bounds = new ArrayList<Rect>(stackViews.size());
- int stackViewsCount = stackViews.size();
- for (int i = 0; i < stackViewsCount; i++) {
- TaskStack stack = stackViews.get(i).getStack();
- Rect sb = stack.stackBounds;
- Rect db = stack.displayBounds;
- Rect ab = availableBounds;
- bounds.add(new Rect(getRelativeCoordinate(ab.left, ab.width(), sb.left, db.width()),
- getRelativeCoordinate(ab.top, ab.height(), sb.top, db.height()),
- getRelativeCoordinate(ab.left, ab.width(), sb.right, db.width()),
- getRelativeCoordinate(ab.top, ab.height(), sb.bottom, db.height())));
- }
- return bounds;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index 0428b48..e04699c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -22,13 +22,15 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
+import android.content.Context;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
-import com.android.systemui.recents.RecentsConfiguration;
/**
* This class facilitates swipe to dismiss. It defines an interface to be implemented by the
@@ -46,6 +48,7 @@
public static final int Y = 1;
private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
+ private Interpolator mLinearOutSlowInInterpolator;
private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
private int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms
@@ -74,13 +77,15 @@
public boolean mAllowSwipeTowardsEnd = true;
private boolean mRtl;
- public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
+ public SwipeHelper(Context context, int swipeDirection, Callback callback, float densityScale,
float pagingTouchSlop) {
mCallback = callback;
mSwipeDirection = swipeDirection;
mVelocityTracker = VelocityTracker.obtain();
mDensityScale = densityScale;
mPagingTouchSlop = pagingTouchSlop;
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
}
public void setDensityScale(float densityScale) {
@@ -265,7 +270,7 @@
ValueAnimator anim = createTranslationAnimation(view, 0);
int duration = SNAP_ANIM_LEN;
anim.setDuration(duration);
- anim.setInterpolator(RecentsConfiguration.getInstance().linearOutSlowInInterpolator);
+ anim.setInterpolator(mLinearOutSlowInInterpolator);
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 1086160..7ce50d8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -17,13 +17,19 @@
package com.android.systemui.recents.views;
import android.app.Activity;
+import android.content.Context;
import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.systemui.R;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
/** Manages the scrims for the various system bars. */
public class SystemBarScrimViews {
+ Context mContext;
RecentsConfiguration mConfig;
View mStatusBarScrimView;
@@ -34,10 +40,22 @@
boolean mHasStatusBarScrim;
boolean mShouldAnimateNavBarScrim;
- public SystemBarScrimViews(Activity activity, RecentsConfiguration config) {
- mConfig = config;
+ int mNavBarScrimEnterDuration;
+
+ Interpolator mFastOutSlowInInterpolator;
+ Interpolator mQuintOutInterpolator;
+
+ public SystemBarScrimViews(Activity activity) {
+ mContext = activity;
+ mConfig = RecentsConfiguration.getInstance();
mStatusBarScrimView = activity.findViewById(R.id.status_bar_scrim);
mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
+ mNavBarScrimEnterDuration = activity.getResources().getInteger(
+ R.integer.recents_nav_bar_scrim_enter_duration);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(activity,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ mQuintOutInterpolator = AnimationUtils.loadInterpolator(activity,
+ com.android.internal.R.interpolator.decelerate_quint);
}
/**
@@ -45,10 +63,11 @@
* the first draw.
*/
public void prepareEnterRecentsAnimation() {
- mHasNavBarScrim = mConfig.hasNavBarScrim();
- mShouldAnimateNavBarScrim = mConfig.shouldAnimateNavBarScrim();
- mHasStatusBarScrim = mConfig.hasStatusBarScrim();
- mShouldAnimateStatusBarScrim = mConfig.shouldAnimateStatusBarScrim();
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ mHasNavBarScrim = launchState.hasNavBarScrim();
+ mShouldAnimateNavBarScrim = launchState.shouldAnimateNavBarScrim();
+ mHasStatusBarScrim = launchState.hasStatusBarScrim();
+ mShouldAnimateStatusBarScrim = launchState.shouldAnimateStatusBarScrim();
mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
View.VISIBLE : View.INVISIBLE);
@@ -60,15 +79,21 @@
* Starts animating the scrim views when entering Recents.
*/
public void startEnterRecentsAnimation() {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ int transitionEnterFromAppDelay = mContext.getResources().getInteger(
+ R.integer.recents_enter_from_app_transition_duration);
+ int transitionEnterFromHomeDelay = mContext.getResources().getInteger(
+ R.integer.recents_enter_from_home_transition_duration);
+
if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
mStatusBarScrimView.animate()
.translationY(0)
- .setStartDelay(mConfig.launchedFromHome ?
- mConfig.transitionEnterFromHomeDelay :
- mConfig.transitionEnterFromAppDelay)
- .setDuration(mConfig.navBarScrimEnterDuration)
- .setInterpolator(mConfig.quintOutInterpolator)
+ .setStartDelay(launchState.launchedFromHome ?
+ transitionEnterFromHomeDelay :
+ transitionEnterFromAppDelay)
+ .setDuration(mNavBarScrimEnterDuration)
+ .setInterpolator(mQuintOutInterpolator)
.withStartAction(new Runnable() {
@Override
public void run() {
@@ -81,11 +106,11 @@
mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
mNavBarScrimView.animate()
.translationY(0)
- .setStartDelay(mConfig.launchedFromHome ?
- mConfig.transitionEnterFromHomeDelay :
- mConfig.transitionEnterFromAppDelay)
- .setDuration(mConfig.navBarScrimEnterDuration)
- .setInterpolator(mConfig.quintOutInterpolator)
+ .setStartDelay(launchState.launchedFromHome ?
+ transitionEnterFromHomeDelay :
+ transitionEnterFromAppDelay)
+ .setDuration(mNavBarScrimEnterDuration)
+ .setInterpolator(mQuintOutInterpolator)
.withStartAction(new Runnable() {
@Override
public void run() {
@@ -101,20 +126,22 @@
* going home).
*/
public void startExitRecentsAnimation() {
+ int taskViewExitToAppDuration = mContext.getResources().getInteger(
+ R.integer.recents_task_exit_to_app_duration);
if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
mStatusBarScrimView.animate()
.translationY(-mStatusBarScrimView.getMeasuredHeight())
.setStartDelay(0)
- .setDuration(mConfig.taskViewExitToAppDuration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
.start();
}
if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
mNavBarScrimView.animate()
.translationY(mNavBarScrimView.getMeasuredHeight())
.setStartDelay(0)
- .setDuration(mConfig.taskViewExitToAppDuration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
.start();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 4e82c8a..b5ad112 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -29,10 +29,10 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -79,8 +79,6 @@
ViewPool<TaskView, Task> mViewPool;
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>();
DozeTrigger mUIDozeTrigger;
- DebugOverlayView mDebugOverlay;
- Rect mTaskStackBounds = new Rect();
DismissView mDismissAllButton;
boolean mDismissAllButtonAnimating;
int mFocusedTaskIndex = -1;
@@ -93,6 +91,7 @@
boolean mStartEnterAnimationRequestedAfterLayout;
boolean mStartEnterAnimationCompleted;
ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
+ Rect mTaskStackBounds = new Rect();
int[] mTmpVisibleRange = new int[2];
float[] mTmpCoord = new float[2];
Matrix mTmpMatrix = new Matrix();
@@ -118,14 +117,17 @@
// Set the stack first
setStack(stack);
mConfig = RecentsConfiguration.getInstance();
- mViewPool = new ViewPool<TaskView, Task>(context, this);
+ mViewPool = new ViewPool<>(context, this);
mInflater = LayoutInflater.from(context);
- mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
- mFilterAlgorithm = new TaskStackViewFilterAlgorithm(mConfig, this, mViewPool);
- mStackScroller = new TaskStackViewScroller(context, mConfig, mLayoutAlgorithm);
+ mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(context, mConfig);
+ mFilterAlgorithm = new TaskStackViewFilterAlgorithm(this, mViewPool);
+ mStackScroller = new TaskStackViewScroller(context, mLayoutAlgorithm);
mStackScroller.setCallbacks(this);
- mTouchHandler = new TaskStackViewTouchHandler(context, this, mConfig, mStackScroller);
- mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
+ mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
+
+ int taskBarDismissDozeDelaySeconds = getResources().getInteger(
+ R.integer.recents_task_bar_dismiss_delay_seconds);
+ mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
@Override
public void run() {
// Show the task bar dismiss buttons
@@ -161,11 +163,6 @@
return mStack;
}
- /** Sets the debug overlay */
- public void setDebugOverlay(DebugOverlayView overlay) {
- mDebugOverlay = overlay;
- }
-
/** Updates the list of task views */
void updateTaskViewsList() {
mTaskViews.clear();
@@ -334,9 +331,6 @@
int[] visibleRange = mTmpVisibleRange;
boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
stackScroll, visibleRange, false);
- if (mDebugOverlay != null) {
- mDebugOverlay.setText("vis[" + visibleRange[1] + "-" + visibleRange[0] + "]");
- }
// Inflate and add the dismiss button if necessary
if (Constants.DebugFlags.App.EnableDismissAll && mDismissAllButton == null) {
@@ -477,11 +471,6 @@
mStackViewsClipDirty = false;
}
- /** The stack insets to apply to the stack contents */
- public void setStackInsetRect(Rect r) {
- mTaskStackBounds.set(r);
- }
-
/** Updates the min and max virtual scroll bounds */
void updateMinMaxScroll(boolean boundScrollToNewMinMax, boolean launchedWithAltTab,
boolean launchedFromHome) {
@@ -692,11 +681,6 @@
return mTouchHandler.onGenericMotionEvent(ev);
}
- /** Returns the region that touch gestures can be started in. */
- Rect getTouchableRegion() {
- return mTaskStackBounds;
- }
-
@Override
public void computeScroll() {
mStackScroller.computeScroll();
@@ -736,6 +720,10 @@
return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks());
}
+ public void setTaskStackBounds(Rect taskStackBounds) {
+ mTaskStackBounds.set(taskStackBounds);
+ }
+
/**
* This is called with the full window width and height to allow stack view children to
* perform the full screen transition down.
@@ -746,10 +734,9 @@
int height = MeasureSpec.getSize(heightMeasureSpec);
// Compute our stack/task rects
- Rect taskStackBounds = new Rect(mTaskStackBounds);
- taskStackBounds.bottom -= mConfig.systemInsets.bottom;
- computeRects(width, height, taskStackBounds, mConfig.launchedWithAltTab,
- mConfig.launchedFromHome);
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ computeRects(width, height, mTaskStackBounds, launchState.launchedWithAltTab,
+ launchState.launchedFromHome);
// If this is the first layout, then scroll to the front of the stack and synchronize the
// stack views immediately to load all the views
@@ -781,9 +768,11 @@
// Measure the dismiss button
if (mDismissAllButton != null) {
int taskRectWidth = mLayoutAlgorithm.mTaskRect.width();
+ int dismissAllButtonHeight = getResources().getDimensionPixelSize(
+ R.dimen.recents_dismiss_all_button_size);
mDismissAllButton.measure(
MeasureSpec.makeMeasureSpec(taskRectWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mConfig.dismissAllButtonSizePx, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(dismissAllButtonHeight, MeasureSpec.EXACTLY));
}
setMeasuredDimension(width, height);
@@ -864,18 +853,19 @@
// When Alt-Tabbing, focus the previous task (but leave the animation until we finish the
// enter animation).
- if (mConfig.launchedWithAltTab) {
- if (mConfig.launchedFromAppWithThumbnail) {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ if (launchState.launchedWithAltTab) {
+ if (launchState.launchedFromAppWithThumbnail) {
focusTask(Math.max(0, mStack.getTaskCount() - 2), false,
- mConfig.launchedHasConfigurationChanged);
+ launchState.launchedHasConfigurationChanged);
} else {
focusTask(Math.max(0, mStack.getTaskCount() - 1), false,
- mConfig.launchedHasConfigurationChanged);
+ launchState.launchedHasConfigurationChanged);
}
}
// Start dozing
- if (!mConfig.multiStackEnabled) {
+ if (!mConfig.multiWindowEnabled) {
mUIDozeTrigger.startDozing();
}
}
@@ -941,7 +931,9 @@
// Start the focus animation when alt-tabbing
ArrayList<Task> tasks = mStack.getTasks();
- if (mConfig.launchedWithAltTab && !mConfig.launchedHasConfigurationChanged &&
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ if (launchState.launchedWithAltTab &&
+ !launchState.launchedHasConfigurationChanged &&
0 <= mFocusedTaskIndex && mFocusedTaskIndex < tasks.size()) {
TaskView tv = getChildViewForTask(tasks.get(mFocusedTaskIndex));
if (tv != null) {
@@ -1132,7 +1124,8 @@
}
// Update the min/max scroll and animate other task views into their new positions
- updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome);
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ updateMinMaxScroll(true, launchState.launchedWithAltTab, launchState.launchedFromHome);
// Offset the stack by as much as the anchor task would otherwise move back
if (pullStackForward) {
@@ -1150,7 +1143,8 @@
TaskView frontTv = getChildViewForTask(newFrontMostTask);
if (frontTv != null) {
frontTv.onTaskBound(newFrontMostTask);
- frontTv.fadeInActionButton(0, mConfig.taskViewEnterFromAppDuration);
+ frontTv.fadeInActionButton(0, getResources().getInteger(
+ R.integer.recents_task_enter_from_app_duration));
}
}
@@ -1299,7 +1293,7 @@
RecentsTaskLoader.getInstance().loadTaskData(task);
// If the doze trigger has already fired, then update the state for this task view
- if (mConfig.multiStackEnabled || mUIDozeTrigger.hasTriggered()) {
+ if (mConfig.multiWindowEnabled || mUIDozeTrigger.hasTriggered()) {
tv.setNoUserInteractionState();
}
@@ -1401,7 +1395,8 @@
if (nextTv != null) {
// Focus the next task, and only animate the visible state if we are launched
// from Alt-Tab
- nextTv.setFocusedTask(mConfig.launchedWithAltTab);
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ nextTv.setFocusedTask(launchState.launchedWithAltTab);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
index 614ca53..e9f6a46 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
@@ -17,8 +17,8 @@
package com.android.systemui.recents.views;
import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
+import com.android.systemui.R;
import java.util.ArrayList;
import java.util.HashMap;
@@ -27,13 +27,10 @@
/* The layout logic for a TaskStackView */
public class TaskStackViewFilterAlgorithm {
- RecentsConfiguration mConfig;
TaskStackView mStackView;
ViewPool<TaskView, Task> mViewPool;
- public TaskStackViewFilterAlgorithm(RecentsConfiguration config, TaskStackView stackView,
- ViewPool<TaskView, Task> viewPool) {
- mConfig = config;
+ public TaskStackViewFilterAlgorithm(TaskStackView stackView, ViewPool<TaskView, Task> viewPool) {
mStackView = stackView;
mViewPool = viewPool;
}
@@ -126,7 +123,8 @@
}
}
}
- return mConfig.filteringNewViewsAnimDuration;
+ return mStackView.getResources().getInteger(
+ R.integer.recents_filter_animate_new_views_duration);
}
/**
@@ -172,7 +170,8 @@
childViewTransformsOut.put(tv, toTransform);
offset++;
}
- return mConfig.filteringCurrentViewsAnimDuration;
+ return mStackView.getResources().getInteger(
+ R.integer.recents_filter_animate_current_views_duration);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index f6df881..8128cac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -16,11 +16,13 @@
package com.android.systemui.recents.views;
+import android.content.Context;
import android.graphics.Rect;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
+import com.android.systemui.R;
import java.util.ArrayList;
import java.util.HashMap;
@@ -48,6 +50,7 @@
}
}
+ Context mContext;
RecentsConfiguration mConfig;
// The various rects that define the stack view
@@ -71,7 +74,8 @@
static float[] xp;
static float[] px;
- public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) {
+ public TaskStackViewLayoutAlgorithm(Context context, RecentsConfiguration config) {
+ mContext = context;
mConfig = config;
// Precompute the path
@@ -87,7 +91,8 @@
mStackVisibleRect.bottom = mViewRect.bottom;
int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
- int heightPadding = mConfig.taskStackTopPaddingPx;
+ int heightPadding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_stack_top_padding);
mStackRect.inset(widthPadding, heightPadding);
// Compute the task rect
@@ -98,7 +103,8 @@
// Update the affiliation offsets
float visibleTaskPct = 0.5f;
- mWithinAffiliationOffset = mConfig.taskBarHeight;
+ mWithinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_bar_height);
mBetweenAffiliationOffset = (int) (visibleTaskPct * mTaskRect.height());
}
@@ -134,8 +140,10 @@
mStackRect.bottom));
float pDismissAllButtonOffset = 0f;
if (Constants.DebugFlags.App.EnableDismissAll) {
+ int dismissAllButtonHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_dismiss_all_button_size);
pDismissAllButtonOffset = pAtBottomOfStackRect -
- screenYToCurveProgress(mStackVisibleRect.bottom - mConfig.dismissAllButtonSizePx);
+ screenYToCurveProgress(mStackVisibleRect.bottom - dismissAllButtonHeight);
}
// Update the task offsets
@@ -177,6 +185,8 @@
// Walk backwards in the task stack and count the number of tasks and visible thumbnails
int taskHeight = mTaskRect.height();
+ int taskBarHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_bar_height);
int numVisibleTasks = 1;
int numVisibleThumbnails = 1;
float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP;
@@ -192,7 +202,7 @@
float scaleAtP = curveProgressToScale(progress);
int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
int screenY = curveProgressToScreenY(progress) + scaleYOffsetAtP;
- boolean hasVisibleThumbnail = (prevScreenY - screenY) > mConfig.taskBarHeight;
+ boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
if (hasVisibleThumbnail) {
numVisibleThumbnails++;
numVisibleTasks++;
@@ -251,8 +261,8 @@
}
float scale = curveProgressToScale(pBounded);
int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
- int minZ = mConfig.taskViewTranslationZMinPx;
- int maxZ = mConfig.taskViewTranslationZMaxPx;
+ int minZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_min);
+ int maxZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_max);
transformOut.scale = scale;
transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
scaleYOffset;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index fabc86d..f0ae87f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -21,9 +21,11 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.OverScroller;
-import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.R;
/* The scrolling logic for a TaskStackView */
public class TaskStackViewScroller {
@@ -31,7 +33,7 @@
public void onScrollChanged(float p);
}
- RecentsConfiguration mConfig;
+ Context mContext;
TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScrollerCallbacks mCb;
@@ -41,10 +43,14 @@
ObjectAnimator mScrollAnimator;
float mFinalAnimatedScroll;
- public TaskStackViewScroller(Context context, RecentsConfiguration config, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
- mConfig = config;
+ Interpolator mLinearOutSlowInInterpolator;
+
+ public TaskStackViewScroller(Context context, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
+ mContext = context;
mScroller = new OverScroller(context);
mLayoutAlgorithm = layoutAlgorithm;
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
setStackScroll(getStackScroll());
}
@@ -140,8 +146,9 @@
mFinalAnimatedScroll = newScroll;
mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
- mScrollAnimator.setDuration(mConfig.taskStackScrollDuration);
- mScrollAnimator.setInterpolator(mConfig.linearOutSlowInInterpolator);
+ mScrollAnimator.setDuration(mContext.getResources().getInteger(
+ R.integer.recents_animate_task_stack_scroll_duration));
+ mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 2e0b80a..86eced8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -26,7 +26,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.R;
import java.util.List;
@@ -34,7 +34,7 @@
class TaskStackViewTouchHandler implements SwipeHelper.Callback {
static int INACTIVE_POINTER_ID = -1;
- RecentsConfiguration mConfig;
+ Context mContext;
TaskStackView mSv;
TaskStackViewScroller mScroller;
VelocityTracker mVelocityTracker;
@@ -62,7 +62,8 @@
boolean mInterceptedBySwipeHelper;
public TaskStackViewTouchHandler(Context context, TaskStackView sv,
- RecentsConfiguration config, TaskStackViewScroller scroller) {
+ TaskStackViewScroller scroller) {
+ mContext = context;
ViewConfiguration configuration = ViewConfiguration.get(context);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
@@ -71,10 +72,9 @@
mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
mSv = sv;
mScroller = scroller;
- mConfig = config;
float densityScale = context.getResources().getDisplayMetrics().density;
- mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
+ mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale, mPagingTouchSlop);
mSwipeHelper.setMinAlpha(1f);
}
@@ -128,16 +128,6 @@
return false;
}
- int action = ev.getAction();
- if (mConfig.multiStackEnabled) {
- // Check if we are within the bounds of the stack view contents
- if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
- if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
- return false;
- }
- }
- }
-
// Pass through to swipe helper if we are swiping
mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
if (mInterceptedBySwipeHelper) {
@@ -146,6 +136,7 @@
boolean wasScrolling = mScroller.isScrolling() ||
(mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
+ int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
@@ -212,16 +203,6 @@
return false;
}
- int action = ev.getAction();
- if (mConfig.multiStackEnabled) {
- // Check if we are within the bounds of the stack view contents
- if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
- if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
- return false;
- }
- }
- }
-
// Pass through to swipe helper if we are swiping
if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
return true;
@@ -230,6 +211,7 @@
// Update the velocity tracker
initVelocityTrackerIfNotExists();
+ int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
@@ -286,7 +268,8 @@
if (Float.compare(overScrollAmount, 0f) != 0) {
// Bound the overscroll to a fixed amount, and inversely scale the y-movement
// relative to how close we are to the max overscroll
- float maxOverScroll = mConfig.taskStackOverscrollPct;
+ float maxOverScroll = mContext.getResources().getFloat(
+ R.dimen.recents_stack_overscroll_percentage);
deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
/ maxOverScroll));
}
@@ -372,7 +355,7 @@
// Shift the tap position toward the center of the task stack and check to see if it would
// have hit a view. The user might have tried to tap on a task and missed slightly.
int shiftedX = x;
- if (x > mSv.getTouchableRegion().centerX()) {
+ if (x > (mSv.getRight() - mSv.getLeft()) / 2) {
shiftedX -= mWindowTouchSlop;
} else {
shiftedX += mWindowTouchSlop;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index cbfe842..bbbaccf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -20,16 +20,25 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.*;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityManager;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
@@ -75,6 +84,10 @@
View mActionButtonView;
TaskViewCallbacks mCb;
+ Interpolator mFastOutSlowInInterpolator;
+ Interpolator mFastOutLinearInInterpolator;
+ Interpolator mQuintOutInterpolator;
+
// Optimizations
ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -99,14 +112,22 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ Resources res = context.getResources();
mConfig = RecentsConfiguration.getInstance();
- mMaxDimScale = mConfig.taskStackMaxDim / 255f;
+ mMaxDimScale = res.getInteger(R.integer.recents_max_task_stack_view_dim) / 255f;
mClipViewInStack = true;
- mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
+ mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius));
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
+ mQuintOutInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.decelerate_quint);
setTaskProgress(getTaskProgress());
setDim(getDim());
if (mConfig.fakeShadows) {
- setBackground(new FakeShadowDrawable(context.getResources(), mConfig));
+ setBackground(new FakeShadowDrawable(res, mConfig));
}
setOutlineProvider(mViewBounds);
}
@@ -159,6 +180,7 @@
int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
+ int taskBarHeight = getResources().getDimensionPixelSize(R.dimen.recents_task_bar_height);
// Measure the content
mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
@@ -166,7 +188,7 @@
// Measure the bar view, and action button
mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(taskBarHeight, MeasureSpec.EXACTLY));
mActionButtonView.measure(
MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST));
@@ -186,7 +208,7 @@
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration,
ValueAnimator.AnimatorUpdateListener updateCallback) {
// Apply the transform
- toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
+ toTransform.applyToTaskView(this, duration, mFastOutSlowInInterpolator, false,
!mConfig.fakeShadows, updateCallback);
// Update the task progress
@@ -238,10 +260,11 @@
* first layout because the actual animation into recents may take a long time. */
void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
boolean occludesLaunchTarget, int offscreenY) {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
int initialDim = getDim();
- if (mConfig.launchedHasConfigurationChanged) {
+ if (launchState.launchedHasConfigurationChanged) {
// Just load the views as-is
- } else if (mConfig.launchedFromAppWithThumbnail) {
+ } else if (launchState.launchedFromAppWithThumbnail) {
if (isTaskViewLaunchTargetTask) {
// Set the dim to 0 so we can animate it in
initialDim = 0;
@@ -252,7 +275,7 @@
setTranslationY(offscreenY);
}
- } else if (mConfig.launchedFromHome) {
+ } else if (launchState.launchedFromHome) {
// Move the task view off screen (below) so we can animate it in
setTranslationY(offscreenY);
setTranslationZ(0);
@@ -267,45 +290,59 @@
/** Animates this task view as it enters recents */
void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ Resources res = mContext.getResources();
final TaskViewTransform transform = ctx.currentTaskTransform;
+ final int transitionEnterFromAppDelay = res.getInteger(
+ R.integer.recents_enter_from_app_transition_duration);
+ final int transitionEnterFromHomeDelay = res.getInteger(
+ R.integer.recents_enter_from_home_transition_duration);
+ final int taskViewEnterFromAppDuration = res.getInteger(
+ R.integer.recents_task_enter_from_app_duration);
+ final int taskViewEnterFromHomeDuration = res.getInteger(
+ R.integer.recents_task_enter_from_home_duration);
+ final int taskViewEnterFromHomeStaggerDelay = res.getInteger(
+ R.integer.recents_task_enter_from_home_stagger_delay);
+ final int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
+ R.dimen.recents_task_view_affiliate_group_enter_offset);
int startDelay = 0;
- if (mConfig.launchedFromAppWithThumbnail) {
+ if (launchState.launchedFromAppWithThumbnail) {
if (mTask.isLaunchTarget) {
// Animate the dim/overlay
if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) {
// Animate the thumbnail alpha before the dim animation (to prevent updating the
// hardware layer)
- mThumbnailView.startEnterRecentsAnimation(mConfig.transitionEnterFromAppDelay,
+ mThumbnailView.startEnterRecentsAnimation(transitionEnterFromAppDelay,
new Runnable() {
@Override
public void run() {
- animateDimToProgress(0, mConfig.taskViewEnterFromAppDuration,
+ animateDimToProgress(0, taskViewEnterFromAppDuration,
ctx.postAnimationTrigger.decrementOnAnimationEnd());
}
});
} else {
// Immediately start the dim animation
- animateDimToProgress(mConfig.transitionEnterFromAppDelay,
- mConfig.taskViewEnterFromAppDuration,
+ animateDimToProgress(transitionEnterFromAppDelay,
+ taskViewEnterFromAppDuration,
ctx.postAnimationTrigger.decrementOnAnimationEnd());
}
ctx.postAnimationTrigger.increment();
// Animate the action button in
- fadeInActionButton(mConfig.transitionEnterFromAppDelay,
- mConfig.taskViewEnterFromAppDuration);
+ fadeInActionButton(transitionEnterFromAppDelay,
+ taskViewEnterFromAppDuration);
} else {
// Animate the task up if it was occluding the launch target
if (ctx.currentTaskOccludesLaunchTarget) {
- setTranslationY(transform.translationY + mConfig.taskViewAffiliateGroupEnterOffsetPx);
+ setTranslationY(transform.translationY + taskViewAffiliateGroupEnterOffset);
setAlpha(0f);
animate().alpha(1f)
.translationY(transform.translationY)
- .setStartDelay(mConfig.transitionEnterFromAppDelay)
+ .setStartDelay(transitionEnterFromAppDelay)
.setUpdateListener(null)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.taskViewEnterFromHomeDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(taskViewEnterFromHomeDuration)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -317,13 +354,13 @@
ctx.postAnimationTrigger.increment();
}
}
- startDelay = mConfig.transitionEnterFromAppDelay;
+ startDelay = transitionEnterFromAppDelay;
- } else if (mConfig.launchedFromHome) {
+ } else if (launchState.launchedFromHome) {
// Animate the tasks up
int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
- int delay = mConfig.transitionEnterFromHomeDelay +
- frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay;
+ int delay = transitionEnterFromHomeDelay +
+ frontIndex * taskViewEnterFromHomeStaggerDelay;
setScaleX(transform.scale);
setScaleY(transform.scale);
@@ -334,9 +371,9 @@
.translationY(transform.translationY)
.setStartDelay(delay)
.setUpdateListener(ctx.updateListener)
- .setInterpolator(mConfig.quintOutInterpolator)
- .setDuration(mConfig.taskViewEnterFromHomeDuration +
- frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay)
+ .setInterpolator(mQuintOutInterpolator)
+ .setDuration(taskViewEnterFromHomeDuration +
+ frontIndex * taskViewEnterFromHomeStaggerDelay)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -373,12 +410,14 @@
/** Animates this task view as it leaves recents by pressing home. */
void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ int taskViewExitToHomeDuration = getResources().getInteger(
+ R.integer.recents_task_exit_to_home_duration);
animate()
.translationY(ctx.offscreenTranslationY)
.setStartDelay(0)
.setUpdateListener(null)
- .setInterpolator(mConfig.fastOutLinearInInterpolator)
- .setDuration(mConfig.taskViewExitToHomeDuration)
+ .setInterpolator(mFastOutLinearInInterpolator)
+ .setDuration(taskViewExitToHomeDuration)
.withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
.start();
ctx.postAnimationTrigger.increment();
@@ -392,6 +431,11 @@
/** Animates this task view as it exits recents */
void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean isLaunchingTask,
boolean occludesLaunchTarget, boolean lockToTask) {
+ final int taskViewExitToAppDuration = mContext.getResources().getInteger(
+ R.integer.recents_task_exit_to_app_duration);
+ final int taskViewAffiliateGroupEnterOffset = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_affiliate_group_enter_offset);
+
if (isLaunchingTask) {
// Animate the thumbnail alpha back into full opacity for the window animation out
mThumbnailView.startLaunchTaskAnimation(postAnimRunnable);
@@ -399,8 +443,8 @@
// Animate the dim
if (mDimAlpha > 0) {
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
- anim.setDuration(mConfig.taskViewExitToAppDuration);
- anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+ anim.setDuration(taskViewExitToAppDuration);
+ anim.setInterpolator(mFastOutLinearInInterpolator);
anim.start();
}
@@ -414,8 +458,8 @@
mActionButtonView.animate()
.alpha(0f)
.setStartDelay(0)
- .setDuration(mConfig.taskViewExitToAppDuration)
- .setInterpolator(mConfig.fastOutLinearInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
+ .setInterpolator(mFastOutLinearInInterpolator)
.start();
} else {
// Hide the dismiss button
@@ -424,11 +468,11 @@
// animate it away first
if (occludesLaunchTarget) {
animate().alpha(0f)
- .translationY(getTranslationY() + mConfig.taskViewAffiliateGroupEnterOffsetPx)
+ .translationY(getTranslationY() + taskViewAffiliateGroupEnterOffset)
.setStartDelay(0)
.setUpdateListener(null)
- .setInterpolator(mConfig.fastOutLinearInInterpolator)
- .setDuration(mConfig.taskViewExitToAppDuration)
+ .setInterpolator(mFastOutLinearInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
.start();
}
}
@@ -436,15 +480,20 @@
/** Animates the deletion of this task view */
void startDeleteTaskAnimation(final Runnable r, int delay) {
+ int taskViewRemoveAnimDuration = getResources().getInteger(
+ R.integer.recents_animate_task_view_remove_duration);
+ int taskViewRemoveAnimTranslationXPx = getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_remove_anim_translation_x);
+
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
- animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
+ animate().translationX(taskViewRemoveAnimTranslationXPx)
.alpha(0f)
.setStartDelay(delay)
.setUpdateListener(null)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.taskViewRemoveAnimDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(taskViewRemoveAnimDuration)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -682,7 +731,7 @@
mHeaderView.mApplicationIcon.setOnClickListener(this);
}
mHeaderView.mDismissButton.setOnClickListener(this);
- if (mConfig.multiStackEnabled) {
+ if (mConfig.multiWindowEnabled) {
mHeaderView.mMoveTaskButton.setOnClickListener(this);
}
mActionButtonView.setOnClickListener(this);
@@ -701,7 +750,7 @@
// Unbind any listeners
mHeaderView.mApplicationIcon.setOnClickListener(null);
mHeaderView.mDismissButton.setOnClickListener(null);
- if (mConfig.multiStackEnabled) {
+ if (mConfig.multiWindowEnabled) {
mHeaderView.mMoveTaskButton.setOnClickListener(null);
}
mActionButtonView.setOnClickListener(null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 353bcbe..f68dd64 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -26,19 +26,21 @@
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -67,6 +69,8 @@
boolean mCurrentPrimaryColorIsDark;
int mCurrentPrimaryColor;
int mBackgroundColor;
+ int mCornerRadius;
+ int mHighlightHeight;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
RippleDrawable mBackground;
@@ -81,6 +85,9 @@
Paint mDimLayerPaint = new Paint();
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
+ Interpolator mFastOutSlowInInterpolator;
+ Interpolator mFastOutLinearInInterpolator;
+
boolean mLayersDisabled;
public TaskViewHeader(Context context) {
@@ -113,13 +120,21 @@
mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
mDismissContentDescription =
context.getString(R.string.accessibility_recents_item_will_be_dismissed);
+ mCornerRadius = getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius);
+ mHighlightHeight = getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_highlight);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
// Configure the highlight paint
if (sHighlightPaint == null) {
sHighlightPaint = new Paint();
sHighlightPaint.setStyle(Paint.Style.STROKE);
- sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx);
- sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor);
+ sHighlightPaint.setStrokeWidth(mHighlightHeight);
+ sHighlightPaint.setColor(context.getColor(R.color.recents_task_bar_highlight_color));
sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
sHighlightPaint.setAntiAlias(true);
}
@@ -154,8 +169,8 @@
@Override
protected void onDraw(Canvas canvas) {
// Draw the highlight at the top edge (but put the bottom edge just out of view)
- float offset = (float) Math.ceil(mConfig.taskViewHighlightPx / 2f);
- float radius = mConfig.taskViewRoundedCornerRadiusPx;
+ float offset = (float) Math.ceil(mHighlightHeight / 2f);
+ float radius = mCornerRadius;
int count = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
@@ -207,16 +222,21 @@
mBackgroundColorDrawable.setColor(t.colorPrimary);
mBackgroundColor = t.colorPrimary;
}
+
+ int taskBarViewLightTextColor = getResources().getColor(
+ R.color.recents_task_bar_light_text_color);
+ int taskBarViewDarkTextColor = getResources().getColor(
+ R.color.recents_task_bar_dark_text_color);
mCurrentPrimaryColor = t.colorPrimary;
mCurrentPrimaryColorIsDark = t.useLightOnPrimaryColor;
mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
- mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor);
+ taskBarViewLightTextColor : taskBarViewDarkTextColor);
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
mLightDismissDrawable : mDarkDismissDrawable);
mDismissButton.setContentDescription(String.format(mDismissContentDescription,
t.contentDescription));
- mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE);
- if (mConfig.multiStackEnabled) {
+ mMoveTaskButton.setVisibility((mConfig.multiWindowEnabled) ? View.VISIBLE : View.INVISIBLE);
+ if (mConfig.multiWindowEnabled) {
updateResizeTaskBarIcon(t);
}
}
@@ -262,12 +282,14 @@
/** Animates this task bar dismiss button when launching a task. */
void startLaunchTaskDismissAnimation() {
if (mDismissButton.getVisibility() == View.VISIBLE) {
+ int taskViewExitToAppDuration = mContext.getResources().getInteger(
+ R.integer.recents_task_exit_to_app_duration);
mDismissButton.animate().cancel();
mDismissButton.animate()
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .setDuration(mConfig.taskViewExitToAppDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(taskViewExitToAppDuration)
.start();
}
}
@@ -280,8 +302,9 @@
mDismissButton.animate()
.alpha(1f)
.setStartDelay(0)
- .setInterpolator(mConfig.fastOutLinearInInterpolator)
- .setDuration(mConfig.taskViewEnterFromAppDuration)
+ .setInterpolator(mFastOutLinearInInterpolator)
+ .setDuration(getResources().getInteger(
+ R.integer.recents_task_enter_from_app_duration))
.start();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 117a7d3..6c83bee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -32,9 +32,11 @@
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
-import com.android.systemui.recents.RecentsConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
+import com.android.systemui.R;
/**
@@ -43,9 +45,8 @@
*/
public class TaskViewThumbnail extends View {
- RecentsConfiguration mConfig;
-
// Drawing
+ int mCornerRadius;
float mDimAlpha;
Matrix mScaleMatrix = new Matrix();
Paint mDrawPaint = new Paint();
@@ -54,6 +55,8 @@
BitmapShader mBitmapShader;
LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
+ Interpolator mFastOutSlowInInterpolator;
+
// Thumbnail alpha
float mThumbnailAlpha;
ValueAnimator mThumbnailAlphaAnimator;
@@ -89,15 +92,18 @@
public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mConfig = RecentsConfiguration.getInstance();
mDrawPaint.setColorFilter(mLightingColorFilter);
mDrawPaint.setFilterBitmap(true);
mDrawPaint.setAntiAlias(true);
+ mCornerRadius = getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
}
@Override
protected void onFinishInflate() {
- mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
+ mThumbnailAlpha = getResources().getFloat(R.dimen.recents_task_view_thumbnail_alpha);
updateThumbnailPaintFilter();
}
@@ -117,8 +123,8 @@
}
// Draw the thumbnail with the rounded corners
canvas.drawRoundRect(0, 0, getWidth(), getHeight(),
- mConfig.taskViewRoundedCornerRadiusPx,
- mConfig.taskViewRoundedCornerRadiusPx, mDrawPaint);
+ mCornerRadius,
+ mCornerRadius, mDrawPaint);
}
/** Sets the thumbnail to a given bitmap. */
@@ -215,8 +221,10 @@
startFadeAnimation(1f, 0, 150, null);
}
} else {
- if (Float.compare(getAlpha(), mConfig.taskViewThumbnailAlpha) != 0) {
- startFadeAnimation(mConfig.taskViewThumbnailAlpha, 0, 150, null);
+ float taskViewThumbnailAlpha = getResources().getFloat(
+ R.dimen.recents_task_view_thumbnail_alpha);
+ if (Float.compare(getAlpha(), taskViewThumbnailAlpha) != 0) {
+ startFadeAnimation(taskViewThumbnailAlpha, 0, 150, null);
}
}
}
@@ -229,20 +237,26 @@
if (isTaskViewLaunchTargetTask) {
mThumbnailAlpha = 1f;
} else {
- mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
+ mThumbnailAlpha = getResources().getFloat(
+ R.dimen.recents_task_view_thumbnail_alpha);
}
updateThumbnailPaintFilter();
}
/** Animates this task thumbnail as it enters Recents. */
void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
- startFadeAnimation(mConfig.taskViewThumbnailAlpha, delay,
- mConfig.taskViewEnterFromAppDuration, postAnimRunnable);
+ float taskViewThumbnailAlpha = getResources().getFloat(
+ R.dimen.recents_task_view_thumbnail_alpha);
+ startFadeAnimation(taskViewThumbnailAlpha, delay,
+ getResources().getInteger(R.integer.recents_task_enter_from_app_duration),
+ postAnimRunnable);
}
/** Animates this task thumbnail as it exits Recents. */
void startLaunchTaskAnimation(Runnable postAnimRunnable) {
- startFadeAnimation(1f, 0, mConfig.taskViewExitToAppDuration, postAnimRunnable);
+ int taskViewExitToAppDuration = mContext.getResources().getInteger(
+ R.integer.recents_task_exit_to_app_duration);
+ startFadeAnimation(1f, 0, taskViewExitToAppDuration, postAnimRunnable);
}
/** Starts a new thumbnail alpha animation. */
@@ -251,7 +265,7 @@
mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha);
mThumbnailAlphaAnimator.setStartDelay(delay);
mThumbnailAlphaAnimator.setDuration(duration);
- mThumbnailAlphaAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mThumbnailAlphaAnimator.setInterpolator(mFastOutSlowInInterpolator);
mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener);
if (postAnimRunnable != null) {
mThumbnailAlphaAnimator.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index c1dfec3..2e3e00c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -34,7 +34,7 @@
import android.view.animation.PathInterpolator;
import com.android.systemui.R;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
+import com.android.systemui.classifier.FalsingManager;
/**
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -129,7 +129,7 @@
private final int mNormalColor;
private final int mLowPriorityColor;
private boolean mIsBelowSpeedBump;
- private LockedPhoneAnalytics mLockedPhoneAnalytics;
+ private FalsingManager mFalsingManager;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -153,7 +153,7 @@
R.color.notification_ripple_color_low_priority);
mNormalRippleColor = context.getColor(
R.color.notification_ripple_untinted_color);
- mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+ mFalsingManager = FalsingManager.getInstance(context);
}
@Override
@@ -222,7 +222,7 @@
makeActive();
postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS);
} else {
- mLockedPhoneAnalytics.onNotificationDoubleTap();
+ mFalsingManager.onNotificationDoubleTap();
boolean performed = performClick();
if (performed) {
removeCallbacks(mTapTimeoutRunnable);
@@ -242,7 +242,7 @@
}
private void makeActive() {
- mLockedPhoneAnalytics.onNotificationActive();
+ mFalsingManager.onNotificationActive();
startActivateAnimation(false /* reverse */);
mActivated = true;
if (mOnActivatedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2c964be..e7a3c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -115,8 +115,7 @@
public abstract class BaseStatusBar extends SystemUI implements
CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
- RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
- NotificationData.Environment {
+ ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment {
public static final String TAG = "StatusBar";
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final boolean MULTIUSER_DEBUG = false;
@@ -577,7 +576,6 @@
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mRecents = getComponent(Recents.class);
- mRecents.setCallback(this);
final Configuration currentConfig = mContext.getResources().getConfiguration();
mLocale = currentConfig.locale;
@@ -1174,11 +1172,6 @@
}
}
- @Override
- public void onVisibilityChanged(boolean visible) {
- // Do nothing
- }
-
/**
* If there is an active heads-up notification and it has a fullscreen intent, fire it now.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index b01a2a8..10d4a96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -112,7 +112,7 @@
public void appTransitionStarting(long startTime, long duration);
public void showAssistDisclosure();
public void startAssist(Bundle args);
- public void onCameraLaunchGestureDetected();
+ public void onCameraLaunchGestureDetected(int source);
}
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -306,10 +306,10 @@
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
synchronized (mList) {
mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
- mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE).sendToTarget();
+ mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE, source, 0).sendToTarget();
}
}
@@ -415,7 +415,7 @@
mCallbacks.startAssist((Bundle) msg.obj);
break;
case MSG_CAMERA_LAUNCH_GESTURE:
- mCallbacks.onCameraLaunchGestureDetected();
+ mCallbacks.onCameraLaunchGestureDetected(msg.arg1);
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index e2304c1..687f6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -29,7 +29,7 @@
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
+import com.android.systemui.classifier.FalsingManager;
/**
* A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand
@@ -55,7 +55,7 @@
private ExpandableView mStartingChild;
private Interpolator mInterpolator;
private float mLastHeight;
- private LockedPhoneAnalytics mLockedPhoneAnalytics;
+ private FalsingManager mFalsingManager;
public DragDownHelper(Context context, View host, ExpandHelper.Callback callback,
DragDownCallback dragDownCallback) {
@@ -67,7 +67,7 @@
mCallback = callback;
mDragDownCallback = dragDownCallback;
mHost = host;
- mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+ mFalsingManager = FalsingManager.getInstance(context);
}
@Override
@@ -87,7 +87,7 @@
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) {
- mLockedPhoneAnalytics.onNotificatonStartDraggingDown();
+ mFalsingManager.onNotificatonStartDraggingDown();
mDraggingDown = true;
captureStartingChild(mInitialTouchX, mInitialTouchY);
mInitialTouchY = y;
@@ -130,7 +130,7 @@
}
return true;
case MotionEvent.ACTION_UP:
- if (mDraggedFarEnough && mDragDownCallback.onDraggedDown(mStartingChild,
+ if (!isFalseTouch() && mDragDownCallback.onDraggedDown(mStartingChild,
(int) (y - mInitialTouchY))) {
if (mStartingChild == null) {
mDragDownCallback.setEmptyDragAmount(0f);
@@ -148,6 +148,13 @@
return false;
}
+ private boolean isFalseTouch() {
+ if (mFalsingManager.isClassiferEnabled()) {
+ return mFalsingManager.isFalseTouch();
+ }
+ return !mDraggedFarEnough;
+ }
+
private void captureStartingChild(float x, float y) {
if (mStartingChild == null) {
mStartingChild = findView(x, y);
@@ -205,7 +212,7 @@
}
private void stopDragging() {
- mLockedPhoneAnalytics.onNotificatonStopDraggingDown();
+ mFalsingManager.onNotificatonStopDraggingDown();
if (mStartingChild != null) {
cancelExpansion(mStartingChild);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 564a60a..210be9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -24,7 +24,6 @@
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -35,7 +34,7 @@
import android.widget.ImageView;
import com.android.systemui.R;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
@@ -111,7 +110,7 @@
!mChildrenExpanded);
}
};
- private LockedPhoneAnalytics mLockedPhoneAnalytics;
+ private FalsingManager mFalsingManager;
private boolean mJustClicked;
@@ -327,7 +326,7 @@
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
- mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+ mFalsingManager = FalsingManager.getInstance(context);
}
/**
@@ -515,7 +514,7 @@
* @param userExpanded whether the user wants this notification to be expanded
*/
public void setUserExpanded(boolean userExpanded) {
- mLockedPhoneAnalytics.setNotificationExpanded();
+ mFalsingManager.setNotificationExpanded();
if (userExpanded && !mExpandable) return;
final boolean wasExpanded = isExpanded();
mHasUserChangedExpansion = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index ac4dee2..5e6fdd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -187,32 +187,41 @@
}
// Try fetching charging time from battery stats.
+ long chargingTimeRemaining = 0;
try {
- long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
- if (chargingTimeRemaining > 0) {
- String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
- mContext, chargingTimeRemaining);
- return mContext.getResources().getString(
- R.string.keyguard_indication_charging_time, chargingTimeFormatted);
- }
+ chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
+
} catch (RemoteException e) {
Log.e(TAG, "Error calling IBatteryStats: ", e);
}
+ final boolean hasChargingTime = chargingTimeRemaining > 0;
- // Fall back to simple charging label.
int chargingId;
switch (mChargingSpeed) {
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
- chargingId = R.string.keyguard_plugged_in_charging_fast;
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_fast_if_translated
+ : R.string.keyguard_plugged_in_charging_fast;
break;
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
- chargingId = R.string.keyguard_plugged_in_charging_slowly;
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_slowly_if_translated
+ : R.string.keyguard_plugged_in_charging_slowly;
break;
default:
- chargingId = R.string.keyguard_plugged_in;
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time
+ : R.string.keyguard_plugged_in;
break;
}
- return mContext.getResources().getString(chargingId);
+
+ if (hasChargingTime) {
+ String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ mContext, chargingTimeRemaining);
+ return mContext.getResources().getString(chargingId, chargingTimeFormatted);
+ } else {
+ return mContext.getResources().getString(chargingId);
+ }
}
KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java
index 2c6987c..f6c1062 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java
@@ -34,11 +34,15 @@
this.pinned = pinned;
}
+ public int getTaskCount() {
+ return tasks == null ? 0 : tasks.size();
+ }
+
/**
* Returns true if the button contains no useful information and should be removed.
*/
public boolean isEmpty() {
- return !pinned && (tasks == null || tasks.isEmpty());
+ return !pinned && getTaskCount() == 0;
}
public void addTask(RecentTaskInfo task) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 5a2fa3b..1d890d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -33,7 +33,7 @@
private static final String TAG = "DozeParameters";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final int MAX_DURATION = 10 * 1000;
+ private static final int MAX_DURATION = 60 * 1000;
private final Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 84082db..2912963 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -93,6 +93,8 @@
private KeyguardViewMediator mKeyguardViewMediator;
private ScrimController mScrimController;
private PhoneStatusBar mPhoneStatusBar;
+ private boolean mGoingToSleep;
+ private int mPendingAuthenticatedUserId = -1;
public FingerprintUnlockController(Context context,
StatusBarWindowManager statusBarWindowManager,
@@ -161,6 +163,10 @@
@Override
public void onFingerprintAuthenticated(int userId) {
+ if (mUpdateMonitor.isGoingToSleep()) {
+ mPendingAuthenticatedUserId = userId;
+ return;
+ }
boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
mMode = calculateMode();
if (!wasDeviceInteractive) {
@@ -205,6 +211,26 @@
mPhoneStatusBar.notifyFpAuthModeChanged();
}
+ @Override
+ public void onStartedGoingToSleep(int why) {
+ mPendingAuthenticatedUserId = -1;
+ }
+
+ @Override
+ public void onFinishedGoingToSleep(int why) {
+ if (mPendingAuthenticatedUserId != -1) {
+
+ // Post this to make sure it's executed after the device is fully locked.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onFingerprintAuthenticated(mPendingAuthenticatedUserId);
+ }
+ });
+ }
+ mPendingAuthenticatedUserId = -1;
+ }
+
public int getMode() {
return mMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 60ebfdf..41adeb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -28,6 +28,7 @@
import android.view.animation.Interpolator;
import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -62,6 +63,7 @@
private Interpolator mAppearInterpolator;
private Interpolator mDisappearInterpolator;
private Animator mSwipeAnimator;
+ private FalsingManager mFalsingManager;
private int mMinBackgroundRadius;
private boolean mMotionCancelled;
private int mTouchTargetSize;
@@ -109,6 +111,7 @@
android.R.interpolator.linear_out_slow_in);
mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.fast_out_linear_in);
+ mFalsingManager = FalsingManager.getInstance(mContext);
}
private void initIcons() {
@@ -322,7 +325,12 @@
float vel = getCurrentVelocity(lastX, lastY);
// We snap back if the current translation is not far enough
- boolean snapBack = isBelowFalsingThreshold();
+ boolean snapBack;
+ if (mFalsingManager.isFalseTouch()) {
+ snapBack = mFalsingManager.isFalseTouch();
+ } else {
+ snapBack = isBelowFalsingThreshold();
+ }
// or if the velocity is in the opposite direction.
boolean velIsInWrongDirection = vel * mTranslation < 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 012dc9c..14176a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -77,6 +77,13 @@
final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
+ public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance";
+ public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture";
+ public static final String CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = "power_double_tap";
+
+ public static final String EXTRA_CAMERA_LAUNCH_SOURCE
+ = "com.android.systemui.camera_launch_source";
+
private static final Intent SECURE_CAMERA_INTENT =
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
@@ -170,7 +177,7 @@
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
return true;
} else if (host == mCameraImageView) {
- launchCamera();
+ launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
return true;
} else if (host == mLeftAffordanceView) {
launchLeftAffordance();
@@ -349,7 +356,7 @@
@Override
public void onClick(View v) {
if (v == mCameraImageView) {
- launchCamera();
+ launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
} else if (v == mLeftAffordanceView) {
launchLeftAffordance();
} if (v == mLockIcon) {
@@ -417,8 +424,9 @@
}
}
- public void launchCamera() {
+ public void launchCamera(String source) {
final Intent intent = getCameraIntent();
+ intent.putExtra(EXTRA_CAMERA_LAUNCH_SOURCE, source);
boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
mContext, intent, KeyguardUpdateMonitor.getCurrentUser());
if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index cbd23bb..99436a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -31,7 +31,7 @@
import com.android.keyguard.R;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.DejankUtils;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
+import com.android.systemui.classifier.FalsingManager;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -50,7 +50,7 @@
private ViewGroup mRoot;
private boolean mShowingSoon;
private int mBouncerPromptReason;
- private LockedPhoneAnalytics mLockedPhoneAnalytics;
+ private FalsingManager mFalsingManager;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -68,11 +68,11 @@
mContainer = container;
mWindowManager = windowManager;
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
- mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(mContext);
+ mFalsingManager = FalsingManager.getInstance(mContext);
}
public void show(boolean resetSecuritySelection) {
- mLockedPhoneAnalytics.onBouncerShown();
+ mFalsingManager.onBouncerShown();
ensureView();
if (resetSecuritySelection) {
// showPrimarySecurityScreen() updates the current security method. This is needed in
@@ -132,7 +132,7 @@
}
public void hide(boolean destroyView) {
- mLockedPhoneAnalytics.onBouncerHidden();
+ mFalsingManager.onBouncerHidden();
cancelShowRunnable();
if (mKeyguardView != null) {
mKeyguardView.cancelDismissAction();
@@ -162,7 +162,7 @@
public void reset() {
cancelShowRunnable();
inflateView();
- mLockedPhoneAnalytics.onBouncerHidden();
+ mFalsingManager.onBouncerHidden();
}
public void onScreenTurnedOff() {
@@ -250,7 +250,7 @@
// We need to show it in case it is secure. If not, it will get dismissed in any case.
mRoot.setVisibility(View.VISIBLE);
- mLockedPhoneAnalytics.onBouncerShown();
+ mFalsingManager.onBouncerShown();
mKeyguardView.requestFocus();
mKeyguardView.onResume();
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 030501b..8e58d14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -166,13 +166,10 @@
if (mAccessibilityController == null) {
return;
}
- boolean trustManagedOrFingerprintAllowed = mUnlockMethodCache.isTrustManaged()
- || KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed();
-
boolean clickToUnlock = mAccessibilityController.isTouchExplorationEnabled();
- boolean clickToForceLock = trustManagedOrFingerprintAllowed
+ boolean clickToForceLock = mUnlockMethodCache.isTrustManaged()
&& !mAccessibilityController.isAccessibilityEnabled();
- boolean longClickToForceLock = trustManagedOrFingerprintAllowed
+ boolean longClickToForceLock = mUnlockMethodCache.isTrustManaged()
&& !clickToForceLock;
setClickable(clickToForceLock || clickToUnlock);
setLongClickable(longClickToForceLock);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 1c9b04f..364e884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -31,9 +31,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
@@ -43,10 +43,15 @@
import android.util.Slog;
import android.view.DragEvent;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.PopupMenu;
import android.widget.Toast;
import com.android.internal.content.PackageMonitor;
@@ -60,8 +65,11 @@
* to the launcher hotseat. Clicking an icon launches or activates the associated activity. A long
* click will trigger a drag to allow the icons to be reordered. As an icon is dragged the other
* icons shift to make space for it to be dropped. These layout changes are animated.
+ * Navigation bar contains both pinned and unpinned apps: pinned in the left part, unpinned in the
+ * right part, with no separator in between.
*/
-class NavigationBarApps extends LinearLayout {
+class NavigationBarApps extends LinearLayout
+ implements NavigationBarAppsModel.OnAppsChangedListener {
public final static boolean DEBUG = false;
private final static String TAG = "NavigationBarApps";
@@ -78,6 +86,7 @@
private final UserManager mUserManager;
private final LayoutInflater mLayoutInflater;
private final AppPackageMonitor mAppPackageMonitor;
+ private final WindowManager mWindowManager;
// This view has two roles:
@@ -103,11 +112,31 @@
}
};
+ // Layout params for the window that contains the anchor for the popup menus.
+ // We need to create a window for a popup menu because the NavBar window is too narrow and can't
+ // contain the menu.
+ private final WindowManager.LayoutParams mPopupAnchorLayoutParams;
+ // View that contains the anchor for popup menus. The view occupies the whole screen, and
+ // has a child that will be moved to make the menu to appear where we need it.
+ private final ViewGroup mPopupAnchor;
+ private final PopupMenu mPopupMenu;
+
+ /**
+ * True if popup menu code is busy with a popup operation.
+ * Attempting to show a popup menu or to add menu items while it's returning true will
+ * corrupt/crash the app.
+ */
+ private boolean mIsPopupInUse = false;
+ private final int [] mClickedIconLocation = new int[2];
+
public NavigationBarApps(Context context, AttributeSet attrs) {
super(context, attrs);
- sAppsModel = new NavigationBarAppsModel(context);
+ if (sAppsModel == null) {
+ sAppsModel = new NavigationBarAppsModel(context);
+ }
mPackageManager = context.getPackageManager();
- mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mLayoutInflater = LayoutInflater.from(context);
mAppPackageMonitor = new AppPackageMonitor();
@@ -131,6 +160,23 @@
} catch (RemoteException e) {
Slog.e(TAG, "registerTaskStackListener failed", e);
}
+
+ mPopupAnchorLayoutParams =
+ new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
+ WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ PixelFormat.TRANSLUCENT);
+ mPopupAnchorLayoutParams.setTitle("ShelfMenuAnchor");
+
+ mPopupAnchor = (ViewGroup) mLayoutInflater.inflate(R.layout.shelf_menu_anchor, null);
+
+ ImageView anchorButton =
+ (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor);
+ mPopupMenu = new PopupMenu(context, anchorButton);
}
// Monitor that catches events like "app uninstalled".
@@ -200,7 +246,7 @@
ComponentName appComponentName = appInfo.getComponentName();
if (!appComponentName.getPackageName().equals(packageName)) continue;
- if (sAppsModel.buildAppLaunchIntent(appInfo) != null) {
+ if (sAppsModel.resolveApp(appInfo) != null) {
continue;
}
@@ -241,6 +287,7 @@
mContext.registerReceiver(mBroadcastReceiver, filter);
mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+ sAppsModel.addOnAppsChangedListener(this);
}
@Override
@@ -248,18 +295,35 @@
super.onDetachedFromWindow();
mContext.unregisterReceiver(mBroadcastReceiver);
mAppPackageMonitor.unregister();
+ sAppsModel.removeOnAppsChangedListener(this);
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ if (mIsPopupInUse && !isShown()) {
+ // Hide the popup if current view became invisible.
+ shutdownPopupMenu();
+ }
}
private void addAppButton(AppButtonData appButtonData) {
- ImageView button = createAppButton(appButtonData);
+ ImageView button = createAppButton();
+ updateApp(button, appButtonData);
addView(button);
+ }
- AppInfo app = appButtonData.appInfo;
- CharSequence appLabel = getAppLabel(mPackageManager, app.getComponentName());
- button.setContentDescription(appLabel);
-
- // Load the icon asynchronously.
- new GetActivityIconTask(mPackageManager, button).execute(appButtonData);
+ private List<AppInfo> getPinnedApps() {
+ List<AppInfo> apps = new ArrayList<AppInfo>();
+ int childCount = getChildCount();
+ for (int i = 0; i != childCount; ++i) {
+ View child = getChildAt(i);
+ AppButtonData appButtonData = (AppButtonData)child.getTag();
+ if (appButtonData == null) continue; // Skip the drag placeholder.
+ if(!appButtonData.pinned) continue;
+ apps.add(appButtonData.appInfo);
+ }
+ return apps;
}
/**
@@ -282,29 +346,21 @@
* Saves pinned apps stored in app icons into the data model.
*/
private void savePinnedApps() {
- List<AppInfo> apps = new ArrayList<AppInfo>();
- int childCount = getChildCount();
- for (int i = 0; i != childCount; ++i) {
- View child = getChildAt(i);
- AppButtonData appButtonData = (AppButtonData)child.getTag();
- if (appButtonData == null) continue; // Skip the drag placeholder.
- if(!appButtonData.pinned) continue;
- apps.add(appButtonData.appInfo);
- }
- sAppsModel.setApps(apps);
+ sAppsModel.setApps(getPinnedApps());
}
/**
* Creates a new ImageView for an app, inflated from R.layout.navigation_bar_app_item.
*/
- private ImageView createAppButton(AppButtonData appButtonData) {
+ private ImageView createAppButton() {
ImageView button = (ImageView) mLayoutInflater.inflate(
R.layout.navigation_bar_app_item, this, false /* attachToRoot */);
+ button.setOnHoverListener(new AppHoverListener());
button.setOnClickListener(new AppClickListener());
+ button.setOnContextClickListener(new AppContextClickListener());
// TODO: Ripple effect. Use either KeyButtonRipple or the default ripple background.
button.setOnLongClickListener(new AppLongClickListener());
button.setOnDragListener(new AppIconDragListener());
- button.setTag(appButtonData);
return button;
}
@@ -323,17 +379,12 @@
* TODO: Cache the labels, perhaps in an LruCache.
*/
@Nullable
- static CharSequence getAppLabel(PackageManager packageManager,
- ComponentName activityName) {
- String packageName = activityName.getPackageName();
- ApplicationInfo info;
- try {
- info = packageManager.getApplicationInfo(packageName, 0x0 /* flags */);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Package not found " + packageName);
- return null;
- }
- return packageManager.getApplicationLabel(info);
+ private CharSequence getAppLabel(AppInfo appInfo) {
+ NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo);
+ if (resolvedApp == null) return null;
+
+ CharSequence unbadgedLabel = resolvedApp.ri.loadLabel(mPackageManager);
+ return mUserManager.getBadgedLabelForUser(unbadgedLabel, appInfo.getUser());
}
/** Helper function to start dragging an app icon (either pinned or recent). */
@@ -420,23 +471,54 @@
* Creates a blank icon-sized View to create an empty space during a drag.
*/
private ImageView createPlaceholderDragView(int index) {
- ImageView button = createAppButton(null);
+ ImageView button = createAppButton();
addView(button, index);
return button;
}
/**
+ * Returns initial index for a new app that doesn't exist in Shelf.
+ * Such apps get created by dragging them into Shelf from other apps or by dragging from Shelf
+ * and then back, or by removing from shelf as an intermediate step of pinning an app via menu.
+ * @param indexHint Initial proposed position for the item.
+ * @param isAppPinned True if the app being dragged is pinned.
+ */
+ int getNewAppIndex(int indexHint, boolean isAppPinned) {
+ int i;
+ if (isAppPinned) {
+ // For a pinned app, find the rightmost position to the left of the target that has a
+ // pinned app. We'll insert to the right of that position.
+ for (i = indexHint; i > 0; --i) {
+ View v = getChildAt(i - 1);
+ AppButtonData targetButtonData = (AppButtonData) v.getTag();
+ if (targetButtonData.pinned) break;
+ }
+ } else {
+ // For an unpinned app, find the leftmost position to the right of the target that has
+ // an unpinned app. We'll insert to the left of that position.
+ int childCount = getChildCount();
+ for (i = indexHint; i < childCount; ++i) {
+ View v = getChildAt(i);
+ AppButtonData targetButtonData = (AppButtonData) v.getTag();
+ if (!targetButtonData.pinned) break;
+ }
+ }
+ return i;
+ }
+
+ /**
* Handles a drag entering an existing icon. Not implemented in the drag listener because it
* needs to use LinearLayout/ViewGroup methods.
*/
private void onDragEnteredIcon(View target) {
if (DEBUG) Slog.d(TAG, "onDragEntered " + indexOfChild(target));
- // If the drag didn't start from an existing icon, add an invisible placeholder to create
- // empty space for the user to drag into.
+ int targetIndex = indexOfChild(target);
+
+ // If the drag didn't start from an existing shelf icon, add an invisible placeholder to
+ // create empty space for the user to drag into.
if (mDragView == null) {
- int placeholderIndex = indexOfChild(target);
- mDragView = createPlaceholderDragView(placeholderIndex);
+ mDragView = createPlaceholderDragView(getNewAppIndex(targetIndex, true));
return;
}
@@ -446,7 +528,28 @@
}
// "Move" the dragged app by removing it and adding it back at the target location.
- int targetIndex = indexOfChild(target);
+ AppButtonData targetButtonData = (AppButtonData) target.getTag();
+ int dragViewIndex = indexOfChild(mDragView);
+ AppButtonData dragViewButtonData = (AppButtonData) mDragView.getTag();
+ // Calculating whether the dragged app is pinned. If the app came from outside if the shelf,
+ // in which case dragViewButtonData == null, it's a new app that we'll pin. Otherwise, the
+ // button data is defined, and we look whether that existing app is pinned.
+ boolean isAppPinned = dragViewButtonData == null || dragViewButtonData.pinned;
+
+ if (dragViewIndex == -1) {
+ // Drag view exists, but is not a child, which means that the drag has started at or
+ // already visited shelf, then left it, and now is entering it again.
+ targetIndex = getNewAppIndex(targetIndex, isAppPinned);
+ } else if (dragViewIndex < targetIndex) {
+ // The dragged app is currently at the left of the view where the drag is.
+ // We shouldn't allow moving a pinned app to the right of the unpinned app.
+ if (!targetButtonData.pinned && isAppPinned) return;
+ } else {
+ // The dragged app is currently at the right of the view where the drag is.
+ // We shouldn't allow moving a unpinned app to the left of the pinned app.
+ if (targetButtonData.pinned && !isAppPinned) return;
+ }
+
// This works, but is subtle:
// * If dragViewIndex > targetIndex then the dragged app is moving from right to left and
// the dragged app will be added in front of the target.
@@ -521,7 +624,7 @@
return null;
}
AppInfo appInfo = new AppInfo(componentName, appUser);
- if (sAppsModel.buildAppLaunchIntent(appInfo) == null) {
+ if (sAppsModel.resolveApp(appInfo) == null) {
return null;
}
return appInfo;
@@ -529,6 +632,9 @@
/** Updates the app at a given view index. */
private void updateApp(ImageView button, AppButtonData appButtonData) {
+ CharSequence appLabel = getAppLabel(appButtonData.appInfo);
+ button.setContentDescription(appLabel);
+
button.setTag(appButtonData);
new GetActivityIconTask(mPackageManager, button).execute(appButtonData);
}
@@ -580,17 +686,148 @@
}
/**
+ * Brings the menu popup to closed state.
+ * Can be called at any stage of the asynchronous process of showing a menu.
+ */
+ private void shutdownPopupMenu() {
+ mWindowManager.removeView(mPopupAnchor);
+ mPopupMenu.dismiss();
+ }
+
+ /**
+ * Shows already prepopulated popup menu using appIcon for anchor location.
+ */
+ private void showPopupMenu(ImageView appIcon) {
+ // Movable view inside the popup anchor view. It serves as the actual anchor for the
+ // menu.
+ final ImageView anchorButton =
+ (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor);
+ // Set same drawable as for the clicked button to have same size.
+ anchorButton.setImageDrawable(appIcon.getDrawable());
+
+ // Move the anchor button to the position of the app button.
+ appIcon.getLocationOnScreen(mClickedIconLocation);
+ anchorButton.setTranslationX(mClickedIconLocation[0]);
+ anchorButton.setTranslationY(mClickedIconLocation[1]);
+
+ final OnAttachStateChangeListener onAttachStateChangeListener =
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mPopupMenu.show();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {}
+ };
+ anchorButton.addOnAttachStateChangeListener(onAttachStateChangeListener);
+
+ mPopupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
+ @Override
+ public void onDismiss(PopupMenu menu) {
+ // FYU: thorough testing for closing menu either by the user or via
+ // shutdownPopupMenu() called at various moments of the menu creation, revealed that
+ // 'onDismiss' is guaranteed to be called after each invocation of showPopupMenu.
+ mWindowManager.removeView(mPopupAnchor);
+ anchorButton.removeOnAttachStateChangeListener(onAttachStateChangeListener);
+ mPopupMenu.setOnDismissListener(null);
+ mPopupMenu.getMenu().clear();
+ mIsPopupInUse = false;
+ }
+ });
+
+ mWindowManager.addView(mPopupAnchor, mPopupAnchorLayoutParams);
+ mIsPopupInUse = true;
+ }
+
+ private void activateTask(int taskPersistentId) {
+ // Launch or bring the activity to front.
+ IActivityManager manager = ActivityManagerNative.getDefault();
+ try {
+ manager.startActivityFromRecents(taskPersistentId, null /* options */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception when activating a recent task", e);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Exception when activating a recent task", e);
+ }
+ }
+
+ /**
+ * Adds to the popup menu items for activating each of tasks in the specified list.
+ */
+ private void populateLaunchMenu(AppButtonData appButtonData) {
+ Menu menu = mPopupMenu.getMenu();
+ int taskCount = appButtonData.getTaskCount();
+ for (int i = 0; i < taskCount; ++i) {
+ final RecentTaskInfo taskInfo = appButtonData.tasks.get(i);
+ MenuItem item = menu.add(getActivityForTask(taskInfo).flattenToShortString());
+ item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ activateTask(taskInfo.persistentId);
+ return true;
+ }
+ });
+ }
+ }
+
+ /**
+ * Shows a task selection menu for clicked or hovered-over apps that have more than 1 running
+ * tasks.
+ */
+ void maybeShowLaunchMenu(ImageView appIcon) {
+ if (mIsPopupInUse) return;
+ AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
+ if (appButtonData.getTaskCount() <= 1) return;
+
+ populateLaunchMenu(appButtonData);
+ showPopupMenu(appIcon);
+ }
+
+ /**
+ * A listener for hovering over an app icon.
+ */
+ private class AppHoverListener implements View.OnHoverListener {
+ private final long DELAY_MILLIS = 1000;
+ private Runnable mShowMenuCallback;
+
+ @Override
+ public boolean onHover(final View v, MotionEvent event) {
+ if (mShowMenuCallback == null) {
+ mShowMenuCallback = new Runnable() {
+ @Override
+ public void run() {
+ maybeShowLaunchMenu((ImageView) v);
+ }
+ };
+ }
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ postDelayed(mShowMenuCallback, DELAY_MILLIS);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ removeCallbacks(mShowMenuCallback);
+ break;
+ }
+ return true;
+ }
+ }
+
+ /**
* A click listener that launches an activity.
*/
private class AppClickListener implements View.OnClickListener {
private void launchApp(AppInfo appInfo, View anchor) {
- Intent launchIntent = sAppsModel.buildAppLaunchIntent(appInfo);
- if (launchIntent == null) {
+ NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo);
+ if (resolvedApp == null) {
Toast.makeText(
getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
return;
}
+ Intent launchIntent = resolvedApp.launchIntent;
+
// Play a scale-up animation while launching the activity.
// TODO: Consider playing a different animation, or no animation, if the activity is
// already open in a visible window. In that case we should move the task to front
@@ -606,29 +843,77 @@
mContext.startActivityAsUser(launchIntent, optsBundle, appInfo.getUser());
}
- private void activateLatestTask(List<RecentTaskInfo> tasks) {
- // 'tasks' is guaranteed to be non-empty.
- int latestTaskPersistentId = tasks.get(0).persistentId;
- // Launch or bring the activity to front.
- IActivityManager manager = ActivityManagerNative.getDefault();
- try {
- manager.startActivityFromRecents(latestTaskPersistentId, null /* options */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception when activating a recent task", e);
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Exception when activating a recent task", e);
+ @Override
+ public void onClick(View v) {
+ AppButtonData appButtonData = (AppButtonData) v.getTag();
+
+ if (appButtonData.getTaskCount() == 0) {
+ launchApp(appButtonData.appInfo, v);
+ } else {
+ // Activate latest task.
+ activateTask(appButtonData.tasks.get(0).persistentId);
+
+ maybeShowLaunchMenu((ImageView) v);
+ }
+ }
+ }
+
+ /**
+ * Context click listener that shows app's context menu.
+ */
+ private class AppContextClickListener implements View.OnContextClickListener {
+ void updateState(ImageView appIcon) {
+ savePinnedApps();
+ if (DEBUG) {
+ AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
+ new GetActivityIconTask(mPackageManager, appIcon).execute(appButtonData);
+ }
+ }
+
+ /**
+ * Adds to the popup menu items for pinning and unpinning the app in the shelf.
+ */
+ void populateContextMenu(final ImageView appIcon) {
+ final AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
+ Menu menu = mPopupMenu.getMenu();
+ if (appButtonData.pinned) {
+ menu.add("Unpin").
+ setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ appButtonData.pinned = false;
+ removeView(appIcon);
+ if (!appButtonData.isEmpty()) {
+ // If the app has running tasks, re-add it to the end of shelf
+ // after unpinning.
+ addView(appIcon);
+ }
+ updateState(appIcon);
+ return true;
+ }
+ });
+ } else {
+ menu.add("Pin").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ appButtonData.pinned = true;
+ removeView(appIcon);
+ // Re-add the pinned icon to the end of the pinned list.
+ addView(appIcon, getNewAppIndex(getChildCount(), true));
+ updateState(appIcon);
+ return true;
+ }
+ });
}
}
@Override
- public void onClick(View v) {
- AppButtonData appButtonData = (AppButtonData)v.getTag();
-
- if (appButtonData.tasks == null || appButtonData.tasks.size() == 0) {
- launchApp(appButtonData.appInfo, v);
- } else {
- activateLatestTask(appButtonData.tasks);
- }
+ public boolean onContextClick(View v) {
+ if (mIsPopupInUse) return true;
+ ImageView appIcon = (ImageView) v;
+ populateContextMenu(appIcon);
+ showPopupMenu(appIcon);
+ return true;
}
}
@@ -785,7 +1070,7 @@
UserHandle taskUser = new UserHandle(task.userId);
AppInfo appInfo = new AppInfo(componentName, taskUser);
- if (sAppsModel.buildAppLaunchIntent(appInfo) == null) {
+ if (sAppsModel.resolveApp(appInfo) == null) {
// If task's activity is not launcheable, fall back to a launch component of the
// task's package.
ComponentName component = getLaunchComponentForPackage(
@@ -818,4 +1103,11 @@
});
}
}
+
+ @Override
+ public void onPinnedAppsChanged() {
+ if (getPinnedApps().equals(sAppsModel.getApps())) return;
+ recreatePinnedAppButtons();
+ updateRecentApps();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
index 832a3e9..d527f29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
@@ -44,6 +44,15 @@
* ComponentName.
*/
class NavigationBarAppsModel {
+ public interface OnAppsChangedListener {
+ void onPinnedAppsChanged();
+ }
+
+ public class ResolvedApp {
+ Intent launchIntent;
+ ResolveInfo ri;
+ }
+
private final static String TAG = "NavigationBarAppsModel";
// Default number of apps to load initially.
@@ -79,6 +88,9 @@
// Apps are represented as an ordered list of app infos.
private List<AppInfo> mApps = new ArrayList<AppInfo>();
+ private List<OnAppsChangedListener> mOnAppsChangedListeners =
+ new ArrayList<OnAppsChangedListener>();
+
// Id of the current user.
private int mCurrentUserId = -1;
@@ -105,8 +117,8 @@
return AppGlobals.getPackageManager();
}
- // Returns a launch intent for a given app info, or null if the app info is unlauncheable.
- public Intent buildAppLaunchIntent(AppInfo appInfo) {
+ // Returns a resolved app info for a given app info, or null if the app info is unlauncheable.
+ public ResolvedApp resolveApp(AppInfo appInfo) {
ComponentName component = appInfo.getComponentName();
int appUserId = appInfo.getUser().getIdentifier();
@@ -153,13 +165,17 @@
0 /* flags */, appUserId);
final int size = apps.size();
for (int i = 0; i < size; ++i) {
- ActivityInfo activityInfo = apps.get(i).activityInfo;
+ ResolveInfo ri = apps.get(i);
+ ActivityInfo activityInfo = ri.activityInfo;
if (activityInfo.packageName.equals(component.getPackageName()) &&
activityInfo.name.equals(component.getClassName())) {
// Found an activity with category launcher that matches
// this component so ok to launch.
launchIntent.setComponent(component);
- return launchIntent;
+ ResolvedApp resolvedApp = new ResolvedApp();
+ resolvedApp.launchIntent = launchIntent;
+ resolvedApp.ri = ri;
+ return resolvedApp;
}
}
@@ -167,6 +183,14 @@
return null;
}
+ public void addOnAppsChangedListener(OnAppsChangedListener listener) {
+ mOnAppsChangedListeners.add(listener);
+ }
+
+ public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
+ mOnAppsChangedListeners.remove(listener);
+ }
+
/**
* Reinitializes the model for a new user.
*/
@@ -239,6 +263,11 @@
public void setApps(List<AppInfo> apps) {
mApps = apps;
savePrefs();
+
+ int size = mOnAppsChangedListeners.size();
+ for (int i = 0; i < size; ++i) {
+ mOnAppsChangedListeners.get(i).onPinnedAppsChanged();
+ }
}
/** Saves the current model to disk. */
@@ -280,7 +309,7 @@
return null;
}
AppInfo appInfo = new AppInfo(componentName, appUser);
- if (buildAppLaunchIntent(appInfo) == null) {
+ if (resolveApp(appInfo) == null) {
return null;
}
return appInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index f37383a..011889a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -261,11 +261,11 @@
private void getIcons(Resources res) {
mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
- mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land);
+ mBackLandIcon = mBackIcon;
mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
- mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime_land);
+ mBackAltLandIcon = mBackAltIcon;
mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent);
- mRecentLandIcon = res.getDrawable(R.drawable.ic_sysbar_recent_land);
+ mRecentLandIcon = mRecentIcon;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index f47ec20..08353cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -22,7 +22,7 @@
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
+import android.app.StatusBarManager;
import android.content.Context;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -50,6 +50,7 @@
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.qs.QSContainer;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -59,7 +60,6 @@
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -204,7 +204,8 @@
private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway;
private boolean mLaunchingAffordance;
- private LockedPhoneAnalytics mLockedPhoneAnalytics;
+ private FalsingManager mFalsingManager;
+ private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@Override
@@ -221,7 +222,7 @@
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(!DEBUG);
- mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+ mFalsingManager = FalsingManager.getInstance(context);
}
public void setStatusBar(PhoneStatusBar bar) {
@@ -481,6 +482,7 @@
mUnlockIconActive = false;
if (!mLaunchingAffordance) {
mAfforanceHelper.reset(false);
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
closeQs();
mStatusBar.dismissPopups();
@@ -693,7 +695,7 @@
}
private boolean flingExpandsQs(float vel) {
- if (isBelowFalsingThreshold()) {
+ if (isFalseTouch()) {
return false;
}
if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -703,8 +705,14 @@
}
}
- private boolean isBelowFalsingThreshold() {
- return !mQsTouchAboveFalsingThreshold && mStatusBarState == StatusBarState.KEYGUARD;
+ private boolean isFalseTouch() {
+ if (mStatusBarState != StatusBarState.KEYGUARD) {
+ return false;
+ }
+ if (mFalsingManager.isClassiferEnabled()) {
+ return mFalsingManager.isFalseTouch();
+ }
+ return !mQsTouchAboveFalsingThreshold;
}
private float getQsExpansionFraction() {
@@ -813,7 +821,7 @@
private void handleQsDown(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN
&& shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
- mLockedPhoneAnalytics.onQsDown();
+ mFalsingManager.onQsDown();
mQsTracking = true;
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
@@ -981,7 +989,7 @@
mQsExpanded = expanded;
updateQsState();
requestPanelHeightUpdate();
- mLockedPhoneAnalytics.setQsExpanded(expanded);
+ mFalsingManager.setQsExpanded(expanded);
mNotificationStackScroller.setInterceptDelegateEnabled(expanded);
mStatusBar.setQsExpanded(expanded);
mQsPanel.setExpanded(expanded);
@@ -1308,7 +1316,7 @@
R.string.accessibility_desc_quick_settings));
mLastAnnouncementWasQuickSettings = true;
}
- if (mQsFullyExpanded && mLockedPhoneAnalytics.shouldEnforceBouncer()) {
+ if (mQsFullyExpanded && mFalsingManager.shouldEnforceBouncer()) {
mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
false /* dismissShade */, true /* afterKeyguardGone */);
}
@@ -1429,7 +1437,7 @@
}
return;
}
- boolean belowFalsingThreshold = isBelowFalsingThreshold();
+ boolean belowFalsingThreshold = isFalseTouch();
if (belowFalsingThreshold) {
vel = 0;
}
@@ -1839,7 +1847,7 @@
@Override
protected void onTrackingStarted() {
- mLockedPhoneAnalytics.onTrackingStarted();
+ mFalsingManager.onTrackingStarted();
super.onTrackingStarted();
if (mQsFullyExpanded) {
mQsExpandImmediate = true;
@@ -1853,7 +1861,7 @@
@Override
protected void onTrackingStopped(boolean expand) {
- mLockedPhoneAnalytics.onTrackingStopped();
+ mFalsingManager.onTrackingStopped();
super.onTrackingStopped(expand);
if (expand) {
mNotificationStackScroller.setOverScrolledPixels(
@@ -1953,8 +1961,8 @@
EventLogTags.writeSysuiLockscreenGesture(
EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER, lengthDp, velocityDp);
- mLockedPhoneAnalytics.onLeftAffordanceOn();
- if (mLockedPhoneAnalytics.shouldEnforceBouncer()) {
+ mFalsingManager.onLeftAffordanceOn();
+ if (mFalsingManager.shouldEnforceBouncer()) {
mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
@Override
public void run() {
@@ -1966,20 +1974,23 @@
mKeyguardBottomArea.launchLeftAffordance();
}
} else {
- EventLogTags.writeSysuiLockscreenGesture(
- EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp);
-
- mLockedPhoneAnalytics.onCameraOn();
- if (mLockedPhoneAnalytics.shouldEnforceBouncer()) {
+ if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
+ mLastCameraLaunchSource)) {
+ EventLogTags.writeSysuiLockscreenGesture(
+ EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA,
+ lengthDp, velocityDp);
+ }
+ mFalsingManager.onCameraOn();
+ if (mFalsingManager.shouldEnforceBouncer()) {
mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
@Override
public void run() {
- mKeyguardBottomArea.launchCamera();
+ mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
}
}, null, true /* dismissShade */, false /* afterKeyguardGone */);
}
else {
- mKeyguardBottomArea.launchCamera();
+ mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
}
}
mStatusBar.startLaunchTransitionTimeout();
@@ -2024,7 +2035,7 @@
@Override
public void onSwipingStarted(boolean rightIcon) {
- mLockedPhoneAnalytics.onAffordanceSwipingStarted(rightIcon);
+ mFalsingManager.onAffordanceSwipingStarted(rightIcon);
boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon
: rightIcon;
if (camera) {
@@ -2037,7 +2048,7 @@
@Override
public void onSwipingAborted() {
- mLockedPhoneAnalytics.onAffordanceSwipingAborted();
+ mFalsingManager.onAffordanceSwipingAborted();
mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
}
@@ -2420,7 +2431,17 @@
return !mDozing;
}
- public void launchCamera(boolean animate) {
+ public void launchCamera(boolean animate, int source) {
+ if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
+ } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
+ } else {
+
+ // Default.
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+ }
+
// If we are launching it when we are occluded already we don't want it to animate,
// nor setting these flags, since the occluded state doesn't change anymore, hence it's
// never reset.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 9cd6ea3..bd16257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -35,6 +35,9 @@
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
+import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.classifier.HumanInteractionClassifier;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
@@ -85,6 +88,7 @@
private ObjectAnimator mPeekAnimator;
private VelocityTrackerInterface mVelocityTracker;
private FlingAnimationUtils mFlingAnimationUtils;
+ private FalsingManager mFalsingManager;
/**
* Whether an instant expand request is currently pending and we are just waiting for layout.
@@ -190,6 +194,7 @@
mLinearOutSlowInInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
mBounceInterpolator = new BounceInterpolator();
+ mFalsingManager = FalsingManager.getInstance(context);
}
protected void loadDimens() {
@@ -605,6 +610,9 @@
if (!mStatusBar.isFalsingThresholdNeeded()) {
return false;
}
+ if (mFalsingManager.isClassiferEnabled()) {
+ return mFalsingManager.isFalseTouch();
+ }
if (!mTouchAboveFalsingThreshold) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index ab8b350..98f5444 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -108,6 +108,7 @@
import com.android.systemui.EventLogTags;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
@@ -131,7 +132,6 @@
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.SpeedBumpView;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -292,6 +292,7 @@
private DozeServiceHost mDozeServiceHost;
private boolean mWakeUpComingFromTouch;
private PointF mWakeUpTouchLocation;
+ private boolean mScreenTurningOn;
int mPixelFormat;
Object mQueueLock = new Object();
@@ -314,6 +315,9 @@
boolean mLeaveOpenOnKeyguardHide;
KeyguardIndicationController mKeyguardIndicationController;
+ // Keyguard is going away soon.
+ private boolean mKeyguardGoingAway;
+ // Keyguard is actually fading away now.
private boolean mKeyguardFadingAway;
private long mKeyguardFadingAwayDelay;
private long mKeyguardFadingAwayDuration;
@@ -487,12 +491,19 @@
private boolean mLaunchTransitionFadingAway;
private ExpandableNotificationRow mDraggedDownRow;
private boolean mLaunchCameraOnScreenTurningOn;
+ private boolean mLaunchCameraOnFinishedGoingToSleep;
+ private int mLastCameraLaunchSource;
private PowerManager.WakeLock mGestureWakeLock;
private Vibrator mVibrator;
// Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
private int mLastLoggedStateFingerprint;
+ /**
+ * If set, the device has started going to sleep but isn't fully non-interactive yet.
+ */
+ protected boolean mStartedGoingToSleep;
+
private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_CARD
| StackViewState.LOCATION_MAIN_AREA;
@@ -594,7 +605,7 @@
private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>();
private RankingMap mLatestRankingMap;
private boolean mNoAnimationOnNextBarModeChange;
- private LockedPhoneAnalytics mLockedPhoneAnalytics;
+ private FalsingManager mFalsingManager;
@Override
public void start() {
@@ -642,7 +653,7 @@
notifyUserAboutHiddenNotifications();
mScreenPinningRequest = new ScreenPinningRequest(mContext);
- mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(mContext);
+ mFalsingManager = FalsingManager.getInstance(mContext);
}
// ================================================================================
@@ -2792,9 +2803,6 @@
if (mNextAlarmController != null) {
mNextAlarmController.dump(fd, pw, args);
}
- if (mAssistManager != null) {
- mAssistManager.dump(fd, pw, args);
- }
if (mSecurityController != null) {
mSecurityController.dump(fd, pw, args);
}
@@ -3030,7 +3038,8 @@
updateNotifications();
resetUserSetupObserver();
setControllerUsers();
- mAssistManager.onUserSwitched(newUserId);
+ clearCurrentMediaNotification();
+ updateMediaMetaData(true);
if (mFullscreenUserSwitcher != null) {
mFullscreenUserSwitcher.onUserSwitched(newUserId);
}
@@ -3597,6 +3606,7 @@
// Treat Keyguard exit animation as an app transition to achieve nice transition for status
// bar.
+ mKeyguardGoingAway = true;
mIconController.appTransitionPending();
}
@@ -3628,6 +3638,7 @@
*/
public void finishKeyguardFadingAway() {
mKeyguardFadingAway = false;
+ mKeyguardGoingAway = false;
}
public void stopWaitingForKeyguardExit() {
@@ -3801,7 +3812,7 @@
}
mState = state;
mGroupManager.setStatusBarState(state);
- mLockedPhoneAnalytics.setStatusBarState(state);
+ mFalsingManager.setStatusBarState(state);
mStatusBarWindowManager.setStatusBarState(state);
updateDozing();
}
@@ -3823,7 +3834,7 @@
}
public void onUnlockHintStarted() {
- mLockedPhoneAnalytics.onUnlockHintStarted();
+ mFalsingManager.onUnlockHintStarted();
mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
}
@@ -3833,17 +3844,17 @@
}
public void onCameraHintStarted() {
- mLockedPhoneAnalytics.onCameraHintStarted();
+ mFalsingManager.onCameraHintStarted();
mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
}
public void onVoiceAssistHintStarted() {
- mLockedPhoneAnalytics.onLeftAffordanceHintStarted();
+ mFalsingManager.onLeftAffordanceHintStarted();
mKeyguardIndicationController.showTransientIndication(R.string.voice_hint);
}
public void onPhoneHintStarted() {
- mLockedPhoneAnalytics.onLeftAffordanceHintStarted();
+ mFalsingManager.onLeftAffordanceHintStarted();
mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
}
@@ -3918,7 +3929,7 @@
row.setUserExpanded(true);
}
boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
- || !mShowLockscreenNotifications || mLockedPhoneAnalytics.shouldEnforceBouncer();
+ || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
mLeaveOpenOnKeyguardHide = true;
showBouncer();
@@ -3959,16 +3970,33 @@
disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */);
}
+ public void onStartedGoingToSleep() {
+ mStartedGoingToSleep = true;
+ }
+
public void onFinishedGoingToSleep() {
mNotificationPanel.onAffordanceLaunchEnded();
releaseGestureWakeLock();
mLaunchCameraOnScreenTurningOn = false;
+ mStartedGoingToSleep = false;
mDeviceInteractive = false;
mWakeUpComingFromTouch = false;
mWakeUpTouchLocation = null;
- mLockedPhoneAnalytics.onScreenOff();
+ mFalsingManager.onScreenOff();
mStackScroller.setAnimationsEnabled(false);
updateVisibleToUser();
+ if (mLaunchCameraOnFinishedGoingToSleep) {
+ mLaunchCameraOnFinishedGoingToSleep = false;
+
+ // This gets executed before we will show Keyguard, so post it in order that the state
+ // is correct.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onCameraLaunchGestureDetected(mLastCameraLaunchSource);
+ }
+ });
+ }
}
public void onStartedWakingUp() {
@@ -3976,22 +4004,25 @@
mStackScroller.setAnimationsEnabled(true);
mNotificationPanel.setTouchDisabled(false);
updateVisibleToUser();
- mLockedPhoneAnalytics.onScreenOn();
}
public void onScreenTurningOn() {
+ mScreenTurningOn = true;
+ mFalsingManager.onScreenTurningOn();
mNotificationPanel.onScreenTurningOn();
if (mLaunchCameraOnScreenTurningOn) {
- mNotificationPanel.launchCamera(false);
+ mNotificationPanel.launchCamera(false, mLastCameraLaunchSource);
mLaunchCameraOnScreenTurningOn = false;
}
}
private void vibrateForCameraGesture() {
- mVibrator.vibrate(750L);
+ // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
+ mVibrator.vibrate(new long[] { 0, 750L }, -1 /* repeat */);
}
public void onScreenTurnedOn() {
+ mScreenTurningOn = false;
mDozeScrimController.onScreenTurnedOn();
}
@@ -4076,8 +4107,7 @@
super.toggleRecents();
}
- @Override
- public void onVisibilityChanged(boolean visible) {
+ public void updateRecentsVisibility(boolean visible) {
// Update the recents visibility flag
if (visible) {
mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
@@ -4113,7 +4143,7 @@
mWakeUpTouchLocation = new PointF(event.getX(), event.getY());
mNotificationPanel.setTouchDisabled(false);
mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
- mLockedPhoneAnalytics.onScreenOnFromTouch();
+ mFalsingManager.onScreenOnFromTouch();
}
}
@@ -4136,9 +4166,8 @@
public void appTransitionStarting(long startTime, long duration) {
// Use own timings when Keyguard is going away, see keyguardGoingAway and
- // setKeyguardFadingAway. When duration is 0, skip this one because no animation is really
- // playing.
- if (!mKeyguardFadingAway && duration > 0) {
+ // setKeyguardFadingAway.
+ if (!mKeyguardGoingAway) {
mIconController.appTransitionStarting(startTime, duration);
}
if (mIconPolicy != null) {
@@ -4147,7 +4176,12 @@
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
+ mLastCameraLaunchSource = source;
+ if (mStartedGoingToSleep) {
+ mLaunchCameraOnFinishedGoingToSleep = true;
+ return;
+ }
if (!mNotificationPanel.canCameraGestureBeLaunched(
mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
return;
@@ -4168,8 +4202,8 @@
mScrimController.dontAnimateBouncerChangesUntilNextFrame();
mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
- if (mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
- mNotificationPanel.launchCamera(mDeviceInteractive /* animate */);
+ if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
+ mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
// we will dismiss us too early since we are waiting on an activity to be drawn and
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e26f423..05f6e57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -164,6 +164,10 @@
}
}
+ public void onStartedGoingToSleep() {
+ mPhoneStatusBar.onStartedGoingToSleep();
+ }
+
public void onFinishedGoingToSleep() {
mDeviceInteractive = false;
mPhoneStatusBar.onFinishedGoingToSleep();
@@ -462,7 +466,7 @@
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
- updateMonitor.sendKeyguardVisibilityChanged(showing && !occluded);
+ updateMonitor.onKeyguardVisibilityChanged(showing && !occluded);
}
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
updateMonitor.sendKeyguardBouncerChanged(bouncerShowing);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index bbf981f..cfd3358 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -36,10 +36,10 @@
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -56,14 +56,14 @@
private PhoneStatusBar mService;
private final Paint mTransparentSrcPaint = new Paint();
- private LockedPhoneAnalytics mLockedPhoneAnalytics;
+ private FalsingManager mFalsingManager;
public StatusBarWindowView(Context context, AttributeSet attrs) {
super(context, attrs);
setMotionEventSplittingEnabled(false);
mTransparentSrcPaint.setColor(0);
mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
- mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+ mFalsingManager = FalsingManager.getInstance(context);
}
@Override
@@ -200,7 +200,7 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- mLockedPhoneAnalytics.onTouchEvent(ev, getWidth(), getHeight());
+ mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) {
// Disallow new pointers while the brightness mirror is visible. This is so that you
// can't touch anything other than the brightness slider while the mirror is showing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 0f9dd5c..e0823b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -182,10 +182,10 @@
@Override
public void onUserSwitched(int newUserId) {
mCurrentUserId = newUserId;
- if (mUserManager.getUserInfo(newUserId).isRestricted()) {
+ final UserInfo newUserInfo = mUserManager.getUserInfo(newUserId);
+ if (newUserInfo.isRestricted()) {
// VPN for a restricted profile is routed through its owner user
- // TODO: http://b/22950929
- mVpnUserId = UserHandle.USER_SYSTEM;
+ mVpnUserId = newUserInfo.restrictedProfileParentId;
} else {
mVpnUserId = mCurrentUserId;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
index c56646f..374408d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
@@ -18,7 +18,7 @@
import com.android.systemui.R;
-class WifiIcons {
+public class WifiIcons {
static final int[][] WIFI_SIGNAL_STRENGTH = {
{ R.drawable.stat_sys_wifi_signal_0,
R.drawable.stat_sys_wifi_signal_1,
@@ -32,7 +32,7 @@
R.drawable.stat_sys_wifi_signal_4_fully }
};
- static final int[][] QS_WIFI_SIGNAL_STRENGTH = {
+ public static final int[][] QS_WIFI_SIGNAL_STRENGTH = {
{ R.drawable.ic_qs_wifi_0,
R.drawable.ic_qs_wifi_1,
R.drawable.ic_qs_wifi_2,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index a1d73d2..5e5f810 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -37,6 +37,7 @@
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -47,7 +48,6 @@
import com.android.systemui.statusbar.SpeedBumpView;
import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -232,7 +232,7 @@
private boolean mForceNoOverlappingRendering;
private NotificationOverflowContainer mOverflowContainer;
private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
- private LockedPhoneAnalytics mLockedPhoneAnalytics;
+ private FalsingManager mFalsingManager;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -266,7 +266,7 @@
mDebugPaint.setStrokeWidth(2);
mDebugPaint.setStyle(Paint.Style.STROKE);
}
- mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+ mFalsingManager = FalsingManager.getInstance(context);
}
@Override
@@ -599,8 +599,8 @@
}
if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
- mLockedPhoneAnalytics.onNotificationDismissed();
- if (mLockedPhoneAnalytics.shouldEnforceBouncer()) {
+ mFalsingManager.onNotificationDismissed();
+ if (mFalsingManager.shouldEnforceBouncer()) {
mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
false /* dismissShade */, true /* afterKeyguardGone */);
}
@@ -631,7 +631,7 @@
}
public void onBeginDrag(View v) {
- mLockedPhoneAnalytics.onNotificatonStartDismissing();
+ mFalsingManager.onNotificatonStartDismissing();
setSwipingInProgress(true);
mAmbientState.onBeginDrag(v);
if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
@@ -658,7 +658,7 @@
}
public void onDragCancelled(View v) {
- mLockedPhoneAnalytics.onNotificatonStopDismissing();
+ mFalsingManager.onNotificatonStopDismissing();
setSwipingInProgress(false);
}
@@ -2156,6 +2156,7 @@
case MotionEvent.ACTION_DOWN: {
final int y = (int) ev.getY();
+ mScrolledToTopOnFirstDown = isScrolledToTop();
if (getChildAtPosition(ev.getX(), y) == null) {
setIsBeingDragged(false);
recycleVelocityTracker();
@@ -2169,7 +2170,6 @@
mLastMotionY = y;
mDownX = (int) ev.getX();
mActivePointerId = ev.getPointerId(0);
- mScrolledToTopOnFirstDown = isScrolledToTop();
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 2587b9f..bbe5dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,7 +171,7 @@
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index b2f527e..da19b06 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -89,7 +89,6 @@
private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
private static final int WAIT_FOR_RIPPLE = 200;
- private static final int UPDATE_ANIMATION_DURATION = 80;
private final Context mContext;
private final H mHandler = new H();
@@ -353,14 +352,6 @@
writer.println(mAccessibility.mFeedbackEnabled);
}
- private static int getImpliedLevel(SeekBar seekBar, int progress) {
- final int m = seekBar.getMax();
- final int n = m / 100 - 1;
- final int level = progress == 0 ? 0
- : progress == m ? (m / 100) : (1 + (int)((progress / (float) m) * n));
- return level;
- }
-
@SuppressLint("InflateParams")
private VolumeRow initRow(final int stream, int iconRes, int iconMuteRes, boolean important) {
final VolumeRow row = new VolumeRow();
@@ -690,7 +681,7 @@
: false;
// update slider max
- final int max = ss.levelMax * 100;
+ final int max = ss.levelMax;
if (max != row.slider.getMax()) {
row.slider.setMax(max);
}
@@ -777,8 +768,7 @@
if (row.tracking) {
return; // don't update if user is sliding
}
- final int progress = row.slider.getProgress();
- final int level = getImpliedLevel(row.slider, progress);
+ final int level = row.slider.getProgress();
final boolean rowVisible = row.view.getVisibility() == View.VISIBLE;
final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt)
< USER_ATTEMPT_GRACE_PERIOD;
@@ -794,33 +784,7 @@
return; // don't clamp if visible
}
}
- final int newProgress = vlevel * 100;
- if (progress != newProgress) {
- if (mShowing && rowVisible) {
- // animate!
- if (row.anim != null && row.anim.isRunning()
- && row.animTargetProgress == newProgress) {
- return; // already animating to the target progress
- }
- // start/update animation
- if (row.anim == null) {
- row.anim = ObjectAnimator.ofInt(row.slider, "progress", progress, newProgress);
- row.anim.setInterpolator(new DecelerateInterpolator());
- } else {
- row.anim.cancel();
- row.anim.setIntValues(progress, newProgress);
- }
- row.animTargetProgress = newProgress;
- row.anim.setDuration(UPDATE_ANIMATION_DURATION);
- row.anim.start();
- } else {
- // update slider directly to clamped value
- if (row.anim != null) {
- row.anim.cancel();
- }
- row.slider.setProgress(newProgress);
- }
- }
+ row.slider.setProgress(vlevel, true);
}
private void recheckH(VolumeRow row) {
@@ -1025,20 +989,19 @@
+ " onProgressChanged " + progress + " fromUser=" + fromUser);
if (!fromUser) return;
if (mRow.ss.levelMin > 0) {
- final int minProgress = mRow.ss.levelMin * 100;
+ final int minProgress = mRow.ss.levelMin;
if (progress < minProgress) {
seekBar.setProgress(minProgress);
progress = minProgress;
}
}
- final int userLevel = getImpliedLevel(seekBar, progress);
- if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
+ if (mRow.ss.level != progress || mRow.ss.muted && progress > 0) {
mRow.userAttempt = SystemClock.uptimeMillis();
- if (mRow.requestedLevel != userLevel) {
- mController.setStreamVolume(mRow.stream, userLevel);
- mRow.requestedLevel = userLevel;
+ if (mRow.requestedLevel != progress) {
+ mController.setStreamVolume(mRow.stream, progress);
+ mRow.requestedLevel = progress;
Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
- userLevel);
+ progress);
}
}
}
@@ -1055,7 +1018,7 @@
if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
mRow.tracking = false;
mRow.userAttempt = SystemClock.uptimeMillis();
- int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
+ final int userLevel = seekBar.getProgress();
Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
if (mRow.ss.level != userLevel) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
@@ -1137,8 +1100,6 @@
private int iconState; // from Events
private boolean cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
private int cachedExpandButtonRes;
- private ObjectAnimator anim; // slider progress animation for non-touch-related updates
- private int animTargetProgress;
private int lastAudibleLevel = 1;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index 32d6805..1cf7a70f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -284,7 +284,7 @@
return changed;
}
- private void onVolumeChangedW(int stream, int flags) {
+ private boolean onVolumeChangedW(int stream, int flags) {
final boolean showUI = (flags & AudioManager.FLAG_SHOW_UI) != 0;
final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
@@ -311,6 +311,7 @@
if (changed && fromKey) {
Events.writeEvent(mContext, Events.EVENT_KEY, stream, lastAudibleStreamVolume);
}
+ return changed;
}
private boolean updateActiveStreamW(int activeStream) {
@@ -797,6 +798,7 @@
if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
+ stream + " devices=" + devices + " oldDevices=" + oldDevices);
changed = checkRoutedToBluetoothW(stream);
+ changed |= onVolumeChangedW(stream, 0);
} else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm="
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index af7ee08..f156607 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -139,6 +139,7 @@
}
public void onConfigurationChanged() {
+ mEndNowButton.setText(mContext.getString(R.string.volume_zen_end_now));
mSpTexts.update();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
index e7a40d7..f51e8ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
@@ -44,7 +44,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
/** Tests for the data model for the navigation bar app icons. */
@@ -109,8 +108,8 @@
};
}
- /** Tests buildAppLaunchIntent(). */
- public void testBuildAppLaunchIntent() {
+ /** Tests resolveApp(). */
+ public void testResolveApp() {
ActivityInfo mockNonExportedActivityInfo = new ActivityInfo();
mockNonExportedActivityInfo.exported = false;
ActivityInfo mockExportedActivityInfo = new ActivityInfo();
@@ -149,25 +148,27 @@
mModel.setCurrentUser(3);
// Unlauncheable (for various reasons) apps.
- assertEquals(null, mModel.buildAppLaunchIntent(
+ assertEquals(null, mModel.resolveApp(
new AppInfo(new ComponentName("package0", "class0"), new UserHandle(3))));
mModel.setCurrentUser(4);
- assertEquals(null, mModel.buildAppLaunchIntent(
+ assertEquals(null, mModel.resolveApp(
new AppInfo(new ComponentName("package1", "class1"), new UserHandle(4))));
mModel.setCurrentUser(5);
- assertEquals(null, mModel.buildAppLaunchIntent(
+ assertEquals(null, mModel.resolveApp(
new AppInfo(new ComponentName("package2", "class2"), new UserHandle(5))));
mModel.setCurrentUser(6);
- assertEquals(null, mModel.buildAppLaunchIntent(
+ assertEquals(null, mModel.resolveApp(
new AppInfo(new ComponentName("package3", "class3"), new UserHandle(6))));
// A launcheable app.
mModel.setCurrentUser(7);
- Intent intent = mModel.buildAppLaunchIntent(
+ NavigationBarAppsModel.ResolvedApp resolvedApp = mModel.resolveApp(
new AppInfo(new ComponentName("package4", "class4"), new UserHandle(7)));
- assertNotNull(intent);
+ assertNotNull(resolvedApp);
+ Intent intent = resolvedApp.launchIntent;
assertEquals(new ComponentName("package4", "class4"), intent.getComponent());
assertEquals("package4", intent.getPackage());
+ assertEquals(ri1, resolvedApp.ri);
}
/** Initializes the model from SharedPreferences for a few app activites. */
@@ -412,4 +413,19 @@
verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
+
+ /** Tests the apps-changed listener. */
+ public void testAppsChangedListeners() {
+ NavigationBarAppsModel.OnAppsChangedListener listener =
+ mock(NavigationBarAppsModel.OnAppsChangedListener.class);
+
+ mModel.addOnAppsChangedListener(listener);
+ mModel.setApps(new ArrayList<AppInfo>());
+ verify(listener).onPinnedAppsChanged();
+ verifyNoMoreInteractions(listener);
+
+ mModel.removeOnAppsChangedListener(listener);
+ mModel.setApps(new ArrayList<AppInfo>());
+ verifyNoMoreInteractions(listener);
+ }
}
diff --git a/packages/VpnDialogs/res/values-nl/strings.xml b/packages/VpnDialogs/res/values-nl/strings.xml
index 0f28016..ae789b2 100644
--- a/packages/VpnDialogs/res/values-nl/strings.xml
+++ b/packages/VpnDialogs/res/values-nl/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindingsverzoek"</string>
- <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding opzetten om netwerkverkeer te controleren. Accepteer het verzoek alleen als u de bron vertrouwt. <br /> <br /> <img src=vpn_icon /> wordt boven aan uw scherm weergegeven wanneer VPN actief is."</string>
+ <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding opzetten om netwerkverkeer te controleren. Accepteer het verzoek alleen als je de bron vertrouwt. <br /> <br /> <img src=vpn_icon /> wordt boven aan je scherm weergegeven wanneer VPN actief is."</string>
<string name="legacy_title" msgid="192936250066580964">"Verbinding met VPN"</string>
<string name="configure" msgid="4905518375574791375">"Configureren"</string>
<string name="disconnect" msgid="971412338304200056">"Verbinding verbreken"</string>
diff --git a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
index 2727338..990d770 100644
--- a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
+++ b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
@@ -130,7 +130,7 @@
return jret;
}
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "createV8ParserNativeLocked", "()Z",
(void*)com_android_pacprocessor_PacNative_createV8ParserNativeLocked},
{ "destroyV8ParserNativeLocked", "()Z",
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 0419d33..8c830e8 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -2494,7 +2494,7 @@
static const char *classPathName = "android/renderscript/RenderScript";
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"_nInit", "()V", (void*)_nInit },
{"nDeviceCreate", "()J", (void*)nDeviceCreate },
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 9f080ca..87a0b80 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -63,6 +63,13 @@
*/
static final int FLAG_FEATURE_FILTER_KEY_EVENTS = 0x00000004;
+ /**
+ * Flag for enabling "Automatically click on mouse stop" feature.
+ *
+ * @see #setEnabledFeatures(int)
+ */
+ static final int FLAG_FEATURE_AUTOCLICK = 0x00000008;
+
private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
@Override
public void run() {
@@ -88,8 +95,6 @@
private final Choreographer mChoreographer;
- private int mCurrentTouchDeviceId;
-
private boolean mInstalled;
private int mEnabledFeatures;
@@ -98,17 +103,19 @@
private ScreenMagnifier mScreenMagnifier;
+ private AutoclickController mAutoclickController;
+
+ private KeyboardInterceptor mKeyboardInterceptor;
+
private EventStreamTransformation mEventHandler;
private MotionEventHolder mEventQueue;
- private boolean mMotionEventSequenceStarted;
+ private EventStreamState mMouseStreamState;
- private boolean mHoverEventSequenceStarted;
+ private EventStreamState mTouchScreenStreamState;
- private boolean mKeyEventSequenceStarted;
-
- private boolean mFilterKeyEvents;
+ private EventStreamState mKeyboardStreamState;
AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
super(context.getMainLooper());
@@ -142,89 +149,95 @@
@Override
public void onInputEvent(InputEvent event, int policyFlags) {
if (DEBUG) {
- Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
}
- if (event instanceof MotionEvent
- && event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
- MotionEvent motionEvent = (MotionEvent) event;
- onMotionEvent(motionEvent, policyFlags);
- } else if (event instanceof KeyEvent
- && event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
- KeyEvent keyEvent = (KeyEvent) event;
- onKeyEvent(keyEvent, policyFlags);
- } else {
- super.onInputEvent(event, policyFlags);
- }
- }
- private void onMotionEvent(MotionEvent event, int policyFlags) {
if (mEventHandler == null) {
super.onInputEvent(event, policyFlags);
return;
}
+
+ EventStreamState state = getEventStreamState(event);
+ if (state == null) {
+ super.onInputEvent(event, policyFlags);
+ return;
+ }
+
+ int eventSource = event.getSource();
if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
- mMotionEventSequenceStarted = false;
- mHoverEventSequenceStarted = false;
- mEventHandler.clear();
+ state.reset();
+ mEventHandler.clearEvents(eventSource);
super.onInputEvent(event, policyFlags);
return;
}
- final int deviceId = event.getDeviceId();
- if (mCurrentTouchDeviceId != deviceId) {
- mCurrentTouchDeviceId = deviceId;
- mMotionEventSequenceStarted = false;
- mHoverEventSequenceStarted = false;
- mEventHandler.clear();
+
+ if (state.updateDeviceId(event.getDeviceId())) {
+ mEventHandler.clearEvents(eventSource);
}
- if (mCurrentTouchDeviceId < 0) {
+
+ if (!state.deviceIdValid()) {
super.onInputEvent(event, policyFlags);
return;
}
- // We do not handle scroll events.
- if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
- super.onInputEvent(event, policyFlags);
- return;
+
+ if (event instanceof MotionEvent) {
+ MotionEvent motionEvent = (MotionEvent) event;
+ processMotionEvent(state, motionEvent, policyFlags);
+ } else if (event instanceof KeyEvent) {
+ KeyEvent keyEvent = (KeyEvent) event;
+ processKeyEvent(state, keyEvent, policyFlags);
}
- // Wait for a down touch event to start processing.
- if (event.isTouchEvent()) {
- if (!mMotionEventSequenceStarted) {
- if (event.getActionMasked() != MotionEvent.ACTION_DOWN) {
- return;
- }
- mMotionEventSequenceStarted = true;
- }
- } else {
- // Wait for an enter hover event to start processing.
- if (!mHoverEventSequenceStarted) {
- if (event.getActionMasked() != MotionEvent.ACTION_HOVER_ENTER) {
- return;
- }
- mHoverEventSequenceStarted = true;
- }
- }
- batchMotionEvent((MotionEvent) event, policyFlags);
}
- private void onKeyEvent(KeyEvent event, int policyFlags) {
- if (!mFilterKeyEvents) {
+ /**
+ * Gets current event stream state associated with an input event.
+ * @return The event stream state that should be used for the event. Null if the event should
+ * not be handled by #AccessibilityInputFilter.
+ */
+ private EventStreamState getEventStreamState(InputEvent event) {
+ if (event instanceof MotionEvent) {
+ if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
+ if (mTouchScreenStreamState == null) {
+ mTouchScreenStreamState = new TouchScreenEventStreamState();
+ }
+ return mTouchScreenStreamState;
+ }
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ if (mMouseStreamState == null) {
+ mMouseStreamState = new MouseEventStreamState();
+ }
+ return mMouseStreamState;
+ }
+ } else if (event instanceof KeyEvent) {
+ if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
+ if (mKeyboardStreamState == null) {
+ mKeyboardStreamState = new KeyboardEventStreamState();
+ }
+ return mKeyboardStreamState;
+ }
+ }
+ return null;
+ }
+
+ private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
+ if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
super.onInputEvent(event, policyFlags);
return;
}
- if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
- mKeyEventSequenceStarted = false;
- super.onInputEvent(event, policyFlags);
+
+ if (!state.shouldProcessMotionEvent(event)) {
return;
}
- // Wait for a down key event to start processing.
- if (!mKeyEventSequenceStarted) {
- if (event.getAction() != KeyEvent.ACTION_DOWN) {
- super.onInputEvent(event, policyFlags);
- return;
- }
- mKeyEventSequenceStarted = true;
+
+ batchMotionEvent(event, policyFlags);
+ }
+
+ private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
+ if (!state.shouldProcessKeyEvent(event)) {
+ return;
}
- mAms.notifyKeyEvent(event, policyFlags);
+ mEventHandler.onKeyEvent(event, policyFlags);
}
private void scheduleProcessBatchedEvents() {
@@ -293,6 +306,11 @@
}
@Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ sendInputEvent(event, policyFlags);
+ }
+
+ @Override
public void onAccessibilityEvent(AccessibilityEvent event) {
// TODO Implement this to inject the accessibility event
// into the accessibility manager service similarly
@@ -305,7 +323,7 @@
}
@Override
- public void clear() {
+ public void clearEvents(int inputSource) {
/* do nothing */
}
@@ -329,43 +347,77 @@
}
private void enableFeatures() {
- mMotionEventSequenceStarted = false;
- mHoverEventSequenceStarted = false;
- if ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
- mEventHandler = mScreenMagnifier = new ScreenMagnifier(mContext,
- Display.DEFAULT_DISPLAY, mAms);
- mEventHandler.setNext(this);
+ resetStreamState();
+
+ if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
+ mAutoclickController = new AutoclickController(mContext);
+ addFirstEventHandler(mAutoclickController);
}
+
if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
mTouchExplorer = new TouchExplorer(mContext, mAms);
- mTouchExplorer.setNext(this);
- if (mEventHandler != null) {
- mEventHandler.setNext(mTouchExplorer);
- } else {
- mEventHandler = mTouchExplorer;
- }
+ addFirstEventHandler(mTouchExplorer);
}
+
+ if ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
+ mScreenMagnifier = new ScreenMagnifier(mContext,
+ Display.DEFAULT_DISPLAY, mAms);
+ addFirstEventHandler(mScreenMagnifier);
+ }
+
if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
- mFilterKeyEvents = true;
+ mKeyboardInterceptor = new KeyboardInterceptor(mAms);
+ addFirstEventHandler(mKeyboardInterceptor);
}
}
+ /**
+ * Adds an event handler to the event handler chain. The handler is added at the beginning of
+ * the chain.
+ *
+ * @param handler The handler to be added to the event handlers list.
+ */
+ private void addFirstEventHandler(EventStreamTransformation handler) {
+ if (mEventHandler != null) {
+ handler.setNext(mEventHandler);
+ } else {
+ handler.setNext(this);
+ }
+ mEventHandler = handler;
+ }
+
void disableFeatures() {
+ if (mAutoclickController != null) {
+ mAutoclickController.onDestroy();
+ mAutoclickController = null;
+ }
if (mTouchExplorer != null) {
- mTouchExplorer.clear();
mTouchExplorer.onDestroy();
mTouchExplorer = null;
}
if (mScreenMagnifier != null) {
- mScreenMagnifier.clear();
mScreenMagnifier.onDestroy();
mScreenMagnifier = null;
}
+ if (mKeyboardInterceptor != null) {
+ mKeyboardInterceptor.onDestroy();
+ mKeyboardInterceptor = null;
+ }
+
mEventHandler = null;
- mKeyEventSequenceStarted = false;
- mMotionEventSequenceStarted = false;
- mHoverEventSequenceStarted = false;
- mFilterKeyEvents = false;
+ resetStreamState();
+ }
+
+ void resetStreamState() {
+ if (mTouchScreenStreamState != null) {
+ mTouchScreenStreamState.reset();
+ }
+ if (mMouseStreamState != null) {
+ mMouseStreamState.reset();
+ }
+ if (mKeyboardStreamState != null) {
+ mKeyboardStreamState.reset();
+ }
}
@Override
@@ -402,4 +454,171 @@
sPool.release(this);
}
}
+
+ /**
+ * Keeps state of event streams observed for an input device with a certain source.
+ * Provides information about whether motion and key events should be processed by accessibility
+ * #EventStreamTransformations. Base implementation describes behaviour for event sources that
+ * whose events should not be handled by a11y event stream transformations.
+ */
+ private static class EventStreamState {
+ private int mDeviceId;
+
+ EventStreamState() {
+ mDeviceId = -1;
+ }
+
+ /**
+ * Updates the ID of the device associated with the state. If the ID changes, resets
+ * internal state.
+ *
+ * @param deviceId Updated input device ID.
+ * @return Whether the device ID has changed.
+ */
+ public boolean updateDeviceId(int deviceId) {
+ if (mDeviceId == deviceId) {
+ return false;
+ }
+ // Reset clears internal state, so make sure it's called before |mDeviceId| is updated.
+ reset();
+ mDeviceId = deviceId;
+ return true;
+ }
+
+ /**
+ * @return Whether device ID is valid.
+ */
+ public boolean deviceIdValid() {
+ return mDeviceId >= 0;
+ }
+
+ /**
+ * Resets the event stream state.
+ */
+ public void reset() {
+ mDeviceId = -1;
+ }
+
+ /**
+ * @return Whether scroll events for device should be handled by event transformations.
+ */
+ public boolean shouldProcessScroll() {
+ return false;
+ }
+
+ /**
+ * @param event An observed motion event.
+ * @return Whether the event should be handled by event transformations.
+ */
+ public boolean shouldProcessMotionEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * @param event An observed key event.
+ * @return Whether the event should be handled by event transformations.
+ */
+ public boolean shouldProcessKeyEvent(KeyEvent event) {
+ return false;
+ }
+ }
+
+ /**
+ * Keeps state of stream of events from a mouse device.
+ */
+ private static class MouseEventStreamState extends EventStreamState {
+ private boolean mMotionSequenceStarted;
+
+ public MouseEventStreamState() {
+ reset();
+ }
+
+ @Override
+ final public void reset() {
+ super.reset();
+ mMotionSequenceStarted = false;
+ }
+
+ @Override
+ final public boolean shouldProcessScroll() {
+ return true;
+ }
+
+ @Override
+ final public boolean shouldProcessMotionEvent(MotionEvent event) {
+ if (mMotionSequenceStarted) {
+ return true;
+ }
+ // Wait for down or move event to start processing mouse events.
+ int action = event.getActionMasked();
+ mMotionSequenceStarted =
+ action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE;
+ return mMotionSequenceStarted;
+ }
+ }
+
+ /**
+ * Keeps state of stream of events from a touch screen device.
+ */
+ private static class TouchScreenEventStreamState extends EventStreamState {
+ private boolean mTouchSequenceStarted;
+ private boolean mHoverSequenceStarted;
+
+ public TouchScreenEventStreamState() {
+ reset();
+ }
+
+ @Override
+ final public void reset() {
+ super.reset();
+ mTouchSequenceStarted = false;
+ mHoverSequenceStarted = false;
+ }
+
+ @Override
+ final public boolean shouldProcessMotionEvent(MotionEvent event) {
+ // Wait for a down touch event to start processing.
+ if (event.isTouchEvent()) {
+ if (mTouchSequenceStarted) {
+ return true;
+ }
+ mTouchSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_DOWN;
+ return mTouchSequenceStarted;
+ }
+
+ // Wait for an enter hover event to start processing.
+ if (mHoverSequenceStarted) {
+ return true;
+ }
+ mHoverSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER;
+ return mHoverSequenceStarted;
+ }
+ }
+
+ /**
+ * Keeps state of stream of events from a keyboard device.
+ */
+ private static class KeyboardEventStreamState extends EventStreamState {
+ private boolean mEventSequenceStarted;
+
+ public KeyboardEventStreamState() {
+ reset();
+ }
+
+ @Override
+ final public void reset() {
+ super.reset();
+ mEventSequenceStarted = false;
+ }
+
+ @Override
+ final public boolean shouldProcessKeyEvent(KeyEvent event) {
+ // Wait for a down key event to start processing.
+ if (mEventSequenceStarted) {
+ return true;
+ }
+ mEventSequenceStarted = event.getAction() == KeyEvent.ACTION_DOWN;
+ return mEventSequenceStarted;
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7051b52..ff2a2ee 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -650,6 +650,7 @@
userState.mIsTouchExplorationEnabled = false;
userState.mIsEnhancedWebAccessibilityEnabled = false;
userState.mIsDisplayMagnificationEnabled = false;
+ userState.mIsAutoclickEnabled = false;
userState.mInstalledServices.add(accessibilityServiceInfo);
userState.mEnabledServices.clear();
userState.mEnabledServices.add(sFakeAccessibilityServiceComponentName);
@@ -700,6 +701,7 @@
userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
userState.mIsEnhancedWebAccessibilityEnabled = false;
userState.mIsDisplayMagnificationEnabled = false;
+ userState.mIsAutoclickEnabled = false;
userState.mEnabledServices.clear();
userState.mEnabledServices.add(service);
userState.mBindingServices.clear();
@@ -965,8 +967,8 @@
List<ResolveInfo> installedServices = mPackageManager.queryIntentServicesAsUser(
new Intent(AccessibilityService.SERVICE_INTERFACE),
- PackageManager.GET_SERVICES
- | PackageManager.GET_META_DATA
+ PackageManager.GET_SERVICES
+ | PackageManager.GET_META_DATA
| PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
mCurrentUserId);
@@ -1281,6 +1283,9 @@
if (userState.mIsFilterKeyEventsEnabled) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
}
+ if (userState.mIsAutoclickEnabled) {
+ flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
+ }
if (flags != 0) {
if (!mHasInputFilter) {
mHasInputFilter = true;
@@ -1485,6 +1490,7 @@
somthingChanged |= readHighTextContrastEnabledSettingLocked(userState);
somthingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
somthingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
+ somthingChanged |= readAutoclickEnabledSettingLocked(userState);
somthingChanged |= readDisplayColorAdjustmentSettingsLocked(userState);
return somthingChanged;
}
@@ -1523,6 +1529,18 @@
return false;
}
+ private boolean readAutoclickEnabledSettingLocked(UserState userState) {
+ final boolean autoclickEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
+ 0, userState.mUserId) == 1;
+ if (autoclickEnabled != userState.mIsAutoclickEnabled) {
+ userState.mIsAutoclickEnabled = autoclickEnabled;
+ return true;
+ }
+ return false;
+ }
+
private boolean readEnhancedWebAccessibilityEnabledChangedLocked(UserState userState) {
final boolean enhancedWeAccessibilityEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
@@ -1671,6 +1689,7 @@
pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
pw.append(", displayMagnificationEnabled="
+ userState.mIsDisplayMagnificationEnabled);
+ pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
if (userState.mUiAutomationService != null) {
pw.append(", ");
userState.mUiAutomationService.dump(fd, pw, args);
@@ -3198,7 +3217,8 @@
case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
- case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: {
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: {
return AccessibilityWindowInfo.TYPE_SYSTEM;
}
@@ -3777,6 +3797,7 @@
public boolean mIsTextHighContrastEnabled;
public boolean mIsEnhancedWebAccessibilityEnabled;
public boolean mIsDisplayMagnificationEnabled;
+ public boolean mIsAutoclickEnabled;
public boolean mIsFilterKeyEventsEnabled;
public boolean mHasDisplayColorAdjustment;
public boolean mAccessibilityFocusOnlyInActiveWindow;
@@ -3841,6 +3862,7 @@
mIsTouchExplorationEnabled = false;
mIsEnhancedWebAccessibilityEnabled = false;
mIsDisplayMagnificationEnabled = false;
+ mIsAutoclickEnabled = false;
}
public void destroyUiAutomationService() {
@@ -3865,6 +3887,9 @@
private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+ private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED);
+
private final Uri mEnabledAccessibilityServicesUri = Settings.Secure.getUriFor(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
@@ -3897,6 +3922,8 @@
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri,
false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(mAutoclickEnabledUri,
+ false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri,
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
@@ -3938,6 +3965,10 @@
if (readDisplayMagnificationEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
+ } else if (mAutoclickEnabledUri.equals(uri)) {
+ if (readAutoclickEnabledSettingLocked(userState)) {
+ onUserStateChangedLocked(userState);
+ }
} else if (mEnabledAccessibilityServicesUri.equals(uri)) {
if (readEnabledAccessibilityServicesLocked(userState)) {
onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
new file mode 100644
index 0000000..8989625
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Implements "Automatically click on mouse stop" feature.
+ *
+ * If enabled, it will observe motion events from mouse source, and send click event sequence
+ * shortly after mouse stops moving. The click will only be performed if mouse movement had been
+ * actually detected.
+ *
+ * Movement detection has tolerance to jitter that may be caused by poor motor control to prevent:
+ * <ul>
+ * <li>Initiating unwanted clicks with no mouse movement.</li>
+ * <li>Autoclick never occurring after mouse arriving at target.</li>
+ * </ul>
+ *
+ * Non-mouse motion events, key events (excluding modifiers) and non-movement mouse events cancel
+ * the automatic click.
+ *
+ * It is expected that each instance will receive mouse events from a single mouse device. User of
+ * the class should handle cases where multiple mouse devices are present.
+ */
+public class AutoclickController implements EventStreamTransformation {
+
+ public static final int DEFAULT_CLICK_DELAY_MS = 600;
+
+ private static final String LOG_TAG = AutoclickController.class.getSimpleName();
+
+ private EventStreamTransformation mNext;
+ private final Context mContext;
+
+ // Lazily created on the first mouse motion event.
+ private ClickScheduler mClickScheduler;
+ private ClickDelayObserver mClickDelayObserver;
+
+ public AutoclickController(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ if (mClickScheduler == null) {
+ Handler handler = new Handler(mContext.getMainLooper());
+ mClickScheduler = new ClickScheduler(handler, DEFAULT_CLICK_DELAY_MS);
+ mClickDelayObserver = new ClickDelayObserver(handler);
+ mClickDelayObserver.start(mContext.getContentResolver(), mClickScheduler);
+ }
+
+ handleMouseMotion(event, policyFlags);
+ } else if (mClickScheduler != null) {
+ mClickScheduler.cancel();
+ }
+
+ if (mNext != null) {
+ mNext.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mClickScheduler != null) {
+ if (KeyEvent.isModifierKey(event.getKeyCode())) {
+ mClickScheduler.updateMetaState(event.getMetaState());
+ } else {
+ mClickScheduler.cancel();
+ }
+ }
+
+ if (mNext != null) {
+ mNext.onKeyEvent(event, policyFlags);
+ }
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
+ }
+
+ @Override
+ public void clearEvents(int inputSource) {
+ if (inputSource == InputDevice.SOURCE_MOUSE && mClickScheduler != null) {
+ mClickScheduler.cancel();
+ }
+
+ if (mNext != null) {
+ mNext.clearEvents(inputSource);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mClickDelayObserver != null) {
+ mClickDelayObserver.stop();
+ mClickDelayObserver = null;
+ }
+ if (mClickScheduler != null) {
+ mClickScheduler.cancel();
+ mClickScheduler = null;
+ }
+ }
+
+ private void handleMouseMotion(MotionEvent event, int policyFlags) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_HOVER_MOVE: {
+ if (event.getPointerCount() == 1) {
+ mClickScheduler.update(event, policyFlags);
+ } else {
+ mClickScheduler.cancel();
+ }
+ } break;
+ // Ignore hover enter and exit.
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_EXIT:
+ break;
+ default:
+ mClickScheduler.cancel();
+ }
+ }
+
+ /**
+ * Observes setting value for autoclick delay, and updates ClickScheduler delay whenever the
+ * setting value changes.
+ */
+ final private static class ClickDelayObserver extends ContentObserver {
+ /** URI used to identify the autoclick delay setting with content resolver. */
+ private final Uri mAutoclickDelaySettingUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY);
+
+ private ContentResolver mContentResolver;
+ private ClickScheduler mClickScheduler;
+
+ public ClickDelayObserver(Handler handler) {
+ super(handler);
+ }
+
+ /**
+ * Starts the observer. And makes sure up-to-date autoclick delay is propagated to
+ * |clickScheduler|.
+ *
+ * @param contentResolver Content resolver that should be observed for setting's value
+ * changes.
+ * @param clickScheduler ClickScheduler that should be updated when click delay changes.
+ * @throws IllegalStateException If internal state is already setup when the method is
+ * called.
+ * @throws NullPointerException If any of the arguments is a null pointer.
+ */
+ public void start(@NonNull ContentResolver contentResolver,
+ @NonNull ClickScheduler clickScheduler) {
+ if (mContentResolver != null || mClickScheduler != null) {
+ throw new IllegalStateException("Observer already started.");
+ }
+ if (contentResolver == null) {
+ throw new NullPointerException("contentResolver not set.");
+ }
+ if (clickScheduler == null) {
+ throw new NullPointerException("clickScheduler not set.");
+ }
+
+ mContentResolver = contentResolver;
+ mClickScheduler = clickScheduler;
+ mContentResolver.registerContentObserver(mAutoclickDelaySettingUri, false, this,
+ UserHandle.USER_ALL);
+
+ // Initialize mClickScheduler's initial delay value.
+ onChange(true, mAutoclickDelaySettingUri);
+ }
+
+ /**
+ * Stops the the observer. Should only be called if the observer has been started.
+ *
+ * @throws IllegalStateException If internal state hasn't yet been initialized by calling
+ * {@link #start}.
+ */
+ public void stop() {
+ if (mContentResolver == null || mClickScheduler == null) {
+ throw new IllegalStateException("ClickDelayObserver not started.");
+ }
+
+ mContentResolver.unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (mAutoclickDelaySettingUri.equals(uri)) {
+ // TODO: Plumb current user id down to here and use getIntForUser.
+ int delay = Settings.Secure.getInt(
+ mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
+ DEFAULT_CLICK_DELAY_MS);
+ mClickScheduler.updateDelay(delay);
+ }
+ }
+ }
+
+ /**
+ * Schedules and performs click event sequence that should be initiated when mouse pointer stops
+ * moving. The click is first scheduled when a mouse movement is detected, and then further
+ * delayed on every sufficient mouse movement.
+ */
+ final private class ClickScheduler implements Runnable {
+ /**
+ * Minimal distance pointer has to move relative to anchor in order for movement not to be
+ * discarded as noise. Anchor is the position of the last MOVE event that was not considered
+ * noise.
+ */
+ private static final double MOVEMENT_SLOPE = 20f;
+
+ /** Whether there is pending click. */
+ private boolean mActive;
+ /** If active, time at which pending click is scheduled. */
+ private long mScheduledClickTime;
+
+ /** Last observed motion event. null if no events have been observed yet. */
+ private MotionEvent mLastMotionEvent;
+ /** Last observed motion event's policy flags. */
+ private int mEventPolicyFlags;
+ /** Current meta state. This value will be used as meta state for click event sequence. */
+ private int mMetaState;
+
+ /**
+ * The current anchor's coordinates. Should be ignored if #mLastMotionEvent is null.
+ * Note that these are not necessary coords of #mLastMotionEvent (because last observed
+ * motion event may have been labeled as noise).
+ */
+ private PointerCoords mAnchorCoords;
+
+ /** Delay that should be used to schedule click. */
+ private int mDelay;
+
+ /** Handler for scheduling delayed operations. */
+ private Handler mHandler;
+
+ private PointerProperties mTempPointerProperties[];
+ private PointerCoords mTempPointerCoords[];
+
+ public ClickScheduler(Handler handler, int delay) {
+ mHandler = handler;
+
+ mLastMotionEvent = null;
+ resetInternalState();
+ mDelay = delay;
+ mAnchorCoords = new PointerCoords();
+ }
+
+ @Override
+ public void run() {
+ long now = SystemClock.uptimeMillis();
+ // Click was rescheduled after task was posted. Post new run task at updated time.
+ if (now < mScheduledClickTime) {
+ mHandler.postDelayed(this, mScheduledClickTime - now);
+ return;
+ }
+
+ sendClick();
+ resetInternalState();
+ }
+
+ /**
+ * Updates properties that should be used for click event sequence initiated by this object,
+ * as well as the time at which click will be scheduled.
+ * Should be called whenever new motion event is observed.
+ *
+ * @param event Motion event whose properties should be used as a base for click event
+ * sequence.
+ * @param policyFlags Policy flags that should be send with click event sequence.
+ */
+ public void update(MotionEvent event, int policyFlags) {
+ mMetaState = event.getMetaState();
+
+ boolean moved = detectMovement(event);
+ cacheLastEvent(event, policyFlags, mLastMotionEvent == null || moved /* useAsAnchor */);
+
+ if (moved) {
+ rescheduleClick(mDelay);
+ }
+ }
+
+ /** Cancels any pending clicks and resets the object state. */
+ public void cancel() {
+ if (!mActive) {
+ return;
+ }
+ resetInternalState();
+ mHandler.removeCallbacks(this);
+ }
+
+ /**
+ * Updates the meta state that should be used for click sequence.
+ */
+ public void updateMetaState(int state) {
+ mMetaState = state;
+ }
+
+ /**
+ * Updates delay that should be used when scheduling clicks. The delay will be used only for
+ * clicks scheduled after this point (pending click tasks are not affected).
+ * @param delay New delay value.
+ */
+ public void updateDelay(int delay) {
+ mDelay = delay;
+ }
+
+ /**
+ * Updates the time at which click sequence should occur.
+ *
+ * @param delay Delay (from now) after which click should occur.
+ */
+ private void rescheduleClick(int delay) {
+ long clickTime = SystemClock.uptimeMillis() + delay;
+ // If there already is a scheduled click at time before the updated time, just update
+ // scheduled time. The click will actually be rescheduled when pending callback is
+ // run.
+ if (mActive && clickTime > mScheduledClickTime) {
+ mScheduledClickTime = clickTime;
+ return;
+ }
+
+ if (mActive) {
+ mHandler.removeCallbacks(this);
+ }
+
+ mActive = true;
+ mScheduledClickTime = clickTime;
+
+ mHandler.postDelayed(this, delay);
+ }
+
+ /**
+ * Updates last observed motion event.
+ *
+ * @param event The last observed event.
+ * @param policyFlags The policy flags used with the last observed event.
+ * @param useAsAnchor Whether the event coords should be used as a new anchor.
+ */
+ private void cacheLastEvent(MotionEvent event, int policyFlags, boolean useAsAnchor) {
+ if (mLastMotionEvent != null) {
+ mLastMotionEvent.recycle();
+ }
+ mLastMotionEvent = MotionEvent.obtain(event);
+ mEventPolicyFlags = policyFlags;
+
+ if (useAsAnchor) {
+ final int pointerIndex = mLastMotionEvent.getActionIndex();
+ mLastMotionEvent.getPointerCoords(pointerIndex, mAnchorCoords);
+ }
+ }
+
+ private void resetInternalState() {
+ mActive = false;
+ if (mLastMotionEvent != null) {
+ mLastMotionEvent.recycle();
+ mLastMotionEvent = null;
+ }
+ mScheduledClickTime = -1;
+ }
+
+ /**
+ * @param event Observed motion event.
+ * @return Whether the event coords are far enough from the anchor for the event not to be
+ * considered noise.
+ */
+ private boolean detectMovement(MotionEvent event) {
+ if (mLastMotionEvent == null) {
+ return false;
+ }
+ final int pointerIndex = event.getActionIndex();
+ float deltaX = mAnchorCoords.x - event.getX(pointerIndex);
+ float deltaY = mAnchorCoords.y - event.getY(pointerIndex);
+ double delta = Math.hypot(deltaX, deltaY);
+ return delta > MOVEMENT_SLOPE;
+ }
+
+ /**
+ * Creates and forwards click event sequence.
+ */
+ private void sendClick() {
+ if (mLastMotionEvent == null || mNext == null) {
+ return;
+ }
+
+ final int pointerIndex = mLastMotionEvent.getActionIndex();
+
+ if (mTempPointerProperties == null) {
+ mTempPointerProperties = new PointerProperties[1];
+ mTempPointerProperties[0] = new PointerProperties();
+ }
+
+ mLastMotionEvent.getPointerProperties(pointerIndex, mTempPointerProperties[0]);
+
+ if (mTempPointerCoords == null) {
+ mTempPointerCoords = new PointerCoords[1];
+ mTempPointerCoords[0] = new PointerCoords();
+ }
+ mLastMotionEvent.getPointerCoords(pointerIndex, mTempPointerCoords[0]);
+
+ final long now = SystemClock.uptimeMillis();
+
+ MotionEvent downEvent = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1,
+ mTempPointerProperties, mTempPointerCoords, mMetaState,
+ MotionEvent.BUTTON_PRIMARY, 1.0f, 1.0f, mLastMotionEvent.getDeviceId(), 0,
+ mLastMotionEvent.getSource(), mLastMotionEvent.getFlags());
+
+ // The only real difference between these two events is the action flag.
+ MotionEvent upEvent = MotionEvent.obtain(downEvent);
+ upEvent.setAction(MotionEvent.ACTION_UP);
+
+ mNext.onMotionEvent(downEvent, downEvent, mEventPolicyFlags);
+ downEvent.recycle();
+
+ mNext.onMotionEvent(upEvent, upEvent, mEventPolicyFlags);
+ upEvent.recycle();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("ClickScheduler: { active=").append(mActive);
+ builder.append(", delay=").append(mDelay);
+ builder.append(", scheduledClickTime=").append(mScheduledClickTime);
+ builder.append(", anchor={x:").append(mAnchorCoords.x);
+ builder.append(", y:").append(mAnchorCoords.y).append("}");
+ builder.append(", metastate=").append(mMetaState);
+ builder.append(", policyFlags=").append(mEventPolicyFlags);
+ builder.append(", lastMotionEvent=").append(mLastMotionEvent);
+ builder.append(" }");
+ return builder.toString();
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/EventStreamTransformation.java b/services/accessibility/java/com/android/server/accessibility/EventStreamTransformation.java
index 8c93e7b..fdc4098 100644
--- a/services/accessibility/java/com/android/server/accessibility/EventStreamTransformation.java
+++ b/services/accessibility/java/com/android/server/accessibility/EventStreamTransformation.java
@@ -68,6 +68,14 @@
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
/**
+ * Receives a key event.
+ *
+ * @param event The key event.
+ * @param policyFlags Policy flags for the event.
+ */
+ public void onKeyEvent(KeyEvent event, int policyFlags);
+
+ /**
* Receives an accessibility event.
*
* @param event The accessibility event.
@@ -82,9 +90,11 @@
public void setNext(EventStreamTransformation next);
/**
- * Clears the internal state of this transformation.
+ * Clears internal state associated with events from specific input source.
+ *
+ * @param inputSource The input source class for which transformation state should be cleared.
*/
- public void clear();
+ public void clearEvents(int inputSource);
/**
* Destroys this transformation.
diff --git a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
new file mode 100644
index 0000000..bbb25af
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Intercepts key events and forwards them to accessibility manager service.
+ */
+public class KeyboardInterceptor implements EventStreamTransformation {
+ private EventStreamTransformation mNext;
+ private AccessibilityManagerService mAms;
+
+ public KeyboardInterceptor(AccessibilityManagerService service) {
+ mAms = service;
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mNext != null) {
+ mNext.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ mAms.notifyKeyEvent(event, policyFlags);
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
+ }
+
+ @Override
+ public void clearEvents(int inputSource) {
+ if (mNext != null) {
+ mNext.clearEvents(inputSource);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
index b4613d6..37276bd 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -37,6 +37,8 @@
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.InputDevice;
+import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
@@ -325,6 +327,12 @@
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
+ if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
+ if (mNext != null) {
+ mNext.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ return;
+ }
mMagnifiedContentInteractonStateHandler.onMotionEvent(event);
switch (mCurrentState) {
case STATE_DELEGATING: {
@@ -348,6 +356,13 @@
}
@Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mNext != null) {
+ mNext.onKeyEvent(event, policyFlags);
+ }
+ }
+
+ @Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (mNext != null) {
mNext.onAccessibilityEvent(event);
@@ -360,22 +375,30 @@
}
@Override
- public void clear() {
- mCurrentState = STATE_DETECTING;
- mDetectingStateHandler.clear();
- mStateViewportDraggingHandler.clear();
- mMagnifiedContentInteractonStateHandler.clear();
+ public void clearEvents(int inputSource) {
+ if (inputSource == InputDevice.SOURCE_TOUCHSCREEN) {
+ clear();
+ }
+
if (mNext != null) {
- mNext.clear();
+ mNext.clearEvents(inputSource);
}
}
@Override
public void onDestroy() {
+ clear();
mScreenStateObserver.destroy();
mWindowManager.setMagnificationCallbacks(null);
}
+ private void clear() {
+ mCurrentState = STATE_DETECTING;
+ mDetectingStateHandler.clear();
+ mStateViewportDraggingHandler.clear();
+ mMagnifiedContentInteractonStateHandler.clear();
+ }
+
private void handleMotionEventStateDelegating(MotionEvent event,
MotionEvent rawEvent, int policyFlags) {
switch (event.getActionMasked()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index f18b5ef..c3f5c43 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -29,6 +29,9 @@
import android.os.Handler;
import android.os.SystemClock;
import android.util.Slog;
+import android.view.GestureDetector;
+import android.view.InputDevice;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
@@ -112,9 +115,6 @@
// Timeout within which we try to detect a tap.
private final int mTapTimeout;
- // Timeout within which we try to detect a double tap.
- private final int mDoubleTapTimeout;
-
// Slop between the down and up tap to be a tap.
private final int mTouchSlop;
@@ -228,7 +228,6 @@
mInjectedPointerTracker = new InjectedPointerTracker();
mTapTimeout = ViewConfiguration.getTapTimeout();
mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
- mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
mHandler = new Handler(context.getMainLooper());
@@ -246,14 +245,29 @@
mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed(
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
mDetermineUserIntentTimeout);
- mDoubleTapDetector = new DoubleTapDetector();
+ mDoubleTapDetector = new DoubleTapDetector(mContext);
final float density = context.getResources().getDisplayMetrics().density;
mScaledMinPointerDistanceToUseMiddleLocation =
(int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density);
}
- public void clear() {
+ @Override
+ public void clearEvents(int inputSource) {
+ if (inputSource == InputDevice.SOURCE_TOUCHSCREEN) {
+ clear();
+ }
+ if (mNext != null) {
+ mNext.clearEvents(inputSource);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ clear();
+ }
+
+ private void clear() {
// If we have not received an event then we are in initial
// state. Therefore, there is not need to clean anything.
MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent();
@@ -262,10 +276,6 @@
}
}
- public void onDestroy() {
- // TODO: Implement
- }
-
private void clear(MotionEvent event, int policyFlags) {
switch (mCurrentState) {
case STATE_TOUCH_EXPLORING: {
@@ -304,9 +314,6 @@
mLongPressingPointerDeltaX = 0;
mLongPressingPointerDeltaY = 0;
mCurrentState = STATE_TOUCH_EXPLORING;
- if (mNext != null) {
- mNext.clear();
- }
mTouchExplorationInProgress = false;
mAms.onTouchInteractionEnd();
}
@@ -318,6 +325,13 @@
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
+ if (mNext != null) {
+ mNext.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ return;
+ }
+
if (DEBUG) {
Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
@@ -344,6 +358,14 @@
}
}
+ @Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mNext != null) {
+ mNext.onKeyEvent(event, policyFlags);
+ }
+ }
+
+ @Override
public void onAccessibilityEvent(AccessibilityEvent event) {
final int eventType = event.getEventType();
@@ -1084,66 +1106,63 @@
}
}
- private class DoubleTapDetector {
- private MotionEvent mDownEvent;
- private MotionEvent mFirstTapEvent;
+ private class DoubleTapDetector extends GestureDetector.SimpleOnGestureListener {
+ private final GestureDetector mGestureDetector;
+ private boolean mFirstTapDetected;
+ private boolean mDoubleTapDetected;
- public void onMotionEvent(MotionEvent event, int policyFlags) {
- final int actionIndex = event.getActionIndex();
- final int action = event.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN: {
- if (mFirstTapEvent != null
- && !GestureUtils.isSamePointerContext(mFirstTapEvent, event)) {
- clear();
- }
- mDownEvent = MotionEvent.obtain(event);
- } break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP: {
- if (mDownEvent == null) {
- return;
- }
- if (!GestureUtils.isSamePointerContext(mDownEvent, event)) {
- clear();
- return;
- }
- if (GestureUtils.isTap(mDownEvent, event, mTapTimeout, mTouchSlop,
- actionIndex)) {
- if (mFirstTapEvent == null || GestureUtils.isTimedOut(mFirstTapEvent,
- event, mDoubleTapTimeout)) {
- mFirstTapEvent = MotionEvent.obtain(event);
- mDownEvent.recycle();
- mDownEvent = null;
- return;
- }
- if (GestureUtils.isMultiTap(mFirstTapEvent, event, mDoubleTapTimeout,
- mDoubleTapSlop, actionIndex)) {
- onDoubleTap(event, policyFlags);
- mFirstTapEvent.recycle();
- mFirstTapEvent = null;
- mDownEvent.recycle();
- mDownEvent = null;
- return;
- }
- mFirstTapEvent.recycle();
- mFirstTapEvent = null;
- } else {
- if (mFirstTapEvent != null) {
- mFirstTapEvent.recycle();
- mFirstTapEvent = null;
- }
- }
- mDownEvent.recycle();
- mDownEvent = null;
- } break;
- }
+ DoubleTapDetector(Context context) {
+ mGestureDetector = new GestureDetector(context, this);
+ mGestureDetector.setOnDoubleTapListener(this);
}
- public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) {
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mDoubleTapDetected = false;
+ break;
+
+ case MotionEvent.ACTION_UP:
+ maybeFinishDoubleTap(event, policyFlags);
+ break;
+ }
+ mGestureDetector.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onDown(MotionEvent event) {
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent event) {
+ mFirstTapDetected = true;
+ return false;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent event) {
+ clear();
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent event) {
+ // The processing of the double tap is deferred until the finger is
+ // lifted, so that we can detect a long press on the second tap.
+ mDoubleTapDetected = true;
+ return true;
+ }
+
+ private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
+ if (!mDoubleTapDetected) {
+ return;
+ }
+
+ clear();
+
// This should never be called when more than two pointers are down.
- if (secondTapUp.getPointerCount() > 2) {
+ if (event.getPointerCount() > 2) {
return;
}
@@ -1159,8 +1178,8 @@
mSendTouchInteractionEndDelayed.forceSendAndRemove();
}
- final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex());
- final int pointerIndex = secondTapUp.findPointerIndex(pointerId);
+ final int pointerId = event.getPointerId(event.getActionIndex());
+ final int pointerIndex = event.findPointerIndex(pointerId);
Point clickLocation = mTempPoint;
final int result = computeClickLocation(clickLocation);
@@ -1171,34 +1190,28 @@
// Do the click.
PointerProperties[] properties = new PointerProperties[1];
properties[0] = new PointerProperties();
- secondTapUp.getPointerProperties(pointerIndex, properties[0]);
+ event.getPointerProperties(pointerIndex, properties[0]);
PointerCoords[] coords = new PointerCoords[1];
coords[0] = new PointerCoords();
coords[0].x = clickLocation.x;
coords[0].y = clickLocation.y;
- MotionEvent event = MotionEvent.obtain(secondTapUp.getDownTime(),
- secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
- coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0,
- secondTapUp.getSource(), secondTapUp.getFlags());
+ MotionEvent click_event = MotionEvent.obtain(event.getDownTime(),
+ event.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
+ coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0,
+ event.getSource(), event.getFlags());
final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
- sendActionDownAndUp(event, policyFlags, targetAccessibilityFocus);
- event.recycle();
+ sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
+ click_event.recycle();
+ return;
}
public void clear() {
- if (mDownEvent != null) {
- mDownEvent.recycle();
- mDownEvent = null;
- }
- if (mFirstTapEvent != null) {
- mFirstTapEvent.recycle();
- mFirstTapEvent = null;
- }
+ mFirstTapDetected = false;
+ mDoubleTapDetected = false;
}
public boolean firstTapDetected() {
- return mFirstTapEvent != null
- && SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout;
+ return mFirstTapDetected;
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index fa87270..02b5b8a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -363,7 +363,8 @@
// ... and see if these are hosts we've been awaiting.
// NOTE: We are backing up and restoring only the owner.
- if (newPackageAdded && userId == UserHandle.USER_OWNER) {
+ // TODO: http://b/22388012
+ if (newPackageAdded && userId == UserHandle.USER_SYSTEM) {
final int uid = getUidForPackage(pkgName, userId);
if (uid >= 0 ) {
resolveHostUidLocked(pkgName, uid);
@@ -1870,19 +1871,28 @@
return false;
}
- private void deleteProviderLocked(Provider provider) {
- int N = provider.widgets.size();
+ // Remove widgets for provider that are hosted in userId.
+ private void deleteWidgetsLocked(Provider provider, int userId) {
+ final int N = provider.widgets.size();
for (int i = N - 1; i >= 0; i--) {
- Widget widget = provider.widgets.remove(i);
- // Call back with empty RemoteViews
- updateAppWidgetInstanceLocked(widget, null, false);
- // clear out references to this appWidgetId
- widget.host.widgets.remove(widget);
- removeWidgetLocked(widget);
- widget.provider = null;
- pruneHostLocked(widget.host);
- widget.host = null;
+ Widget widget = provider.widgets.get(i);
+ if (userId == UserHandle.USER_ALL
+ || userId == widget.host.getUserId()) {
+ provider.widgets.remove(i);
+ // Call back with empty RemoteViews
+ updateAppWidgetInstanceLocked(widget, null, false);
+ // clear out references to this appWidgetId
+ widget.host.widgets.remove(widget);
+ removeWidgetLocked(widget);
+ widget.provider = null;
+ pruneHostLocked(widget.host);
+ widget.host = null;
+ }
}
+ }
+
+ private void deleteProviderLocked(Provider provider) {
+ deleteWidgetsLocked(provider, UserHandle.USER_ALL);
mProviders.remove(provider);
// no need to send the DISABLE broadcast, since the receiver is gone anyway
@@ -2729,7 +2739,7 @@
Host host = lookupHostLocked(oldHostId);
if (host != null) {
final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE,
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
if (uid >= 0) {
host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE);
}
@@ -2750,7 +2760,7 @@
private static AtomicFile getSavedStateFile(int userId) {
File dir = Environment.getUserSystemDirectory(userId);
File settingsFile = getStateFile(userId);
- if (!settingsFile.exists() && userId == UserHandle.USER_OWNER) {
+ if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) {
if (!dir.exists()) {
dir.mkdirs();
}
@@ -2930,6 +2940,19 @@
return providersUpdated;
}
+ // Remove widgets for provider in userId that are hosted in parentUserId
+ private void removeWidgetsForPackageLocked(String pkgName, int userId, int parentUserId) {
+ final int N = mProviders.size();
+ for (int i = 0; i < N; ++i) {
+ Provider provider = mProviders.get(i);
+ if (pkgName.equals(provider.info.provider.getPackageName())
+ && provider.getUserId() == userId
+ && provider.widgets.size() > 0) {
+ deleteWidgetsLocked(provider, parentUserId);
+ }
+ }
+ }
+
private boolean removeProvidersForPackageLocked(String pkgName, int userId) {
boolean removed = false;
@@ -3041,14 +3064,14 @@
userId, null);
}
- // Some packages are no longer whitelisted.
+ // Remove widgets from hosts in parent user for packages not in the whitelist
final int removedCount = previousPackages.size();
for (int i = 0; i < removedCount; ++i) {
- providersChanged |= removeProvidersForPackageLocked(
- previousPackages.valueAt(i), userId);
+ removeWidgetsForPackageLocked(previousPackages.valueAt(i),
+ userId, parentId);
}
- if (providersChanged) {
+ if (providersChanged || removedCount > 0) {
saveGroupStateAsync(userId);
scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index ff02b94..2264c69 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -338,7 +338,7 @@
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
- sInstance.initialize(UserHandle.USER_OWNER);
+ sInstance.initialize(UserHandle.USER_SYSTEM);
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
ContentResolver r = sInstance.mContext.getContentResolver();
boolean areEnabled = Settings.Secure.getInt(r,
@@ -934,7 +934,7 @@
case MSG_WIDGET_BROADCAST:
{
final Intent intent = (Intent) msg.obj;
- mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
break;
}
}
@@ -1092,8 +1092,9 @@
if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
// Find all transport hosts and bind to their services
+ // TODO: http://b/22388012
List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
- mTransportServiceIntent, 0, UserHandle.USER_OWNER);
+ mTransportServiceIntent, 0, UserHandle.USER_SYSTEM);
if (DEBUG) {
Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
}
@@ -1953,8 +1954,9 @@
void checkForTransportAndBind(PackageInfo pkgInfo) {
Intent intent = new Intent(mTransportServiceIntent)
.setPackage(pkgInfo.packageName);
+ // TODO: http://b/22388012
List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
- intent, 0, UserHandle.USER_OWNER);
+ intent, 0, UserHandle.USER_SYSTEM);
if (hosts != null) {
final int N = hosts.size();
for (int i = 0; i < N; i++) {
@@ -2002,9 +2004,10 @@
mContext.unbindService(connection);
}
}
+ // TODO: http://b/22388012
return mContext.bindServiceAsUser(intent,
connection, Context.BIND_AUTO_CREATE,
- UserHandle.OWNER);
+ UserHandle.SYSTEM);
}
// Add the backup agents in the given packages to our set of known backup participants.
@@ -2853,8 +2856,9 @@
private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
throws IOException {
+ // TODO: http://b/22388012
byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
// has the widget state changed since last time?
final File widgetFile = new File(mStateDir, pkgName + "_widget");
final boolean priorStateExists = widgetFile.exists();
@@ -3398,8 +3402,9 @@
&& ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
(app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ // TODO: http://b/22388012
byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
final int token = generateToken();
FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
@@ -3463,7 +3468,8 @@
// Save associated .obb content if it exists and we did save the apk
// check for .obb and save those too
- final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_OWNER);
+ // TODO: http://b/22388012
+ final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_SYSTEM);
final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0];
if (obbDir != null) {
if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
@@ -3803,8 +3809,9 @@
// If we're doing widget state as well, ensure that we have all the involved
// host & provider packages in the set
if (mDoWidgets) {
+ // TODO: http://b/22388012
List<String> pkgs =
- AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_OWNER);
+ AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_SYSTEM);
if (pkgs != null) {
if (MORE_DEBUG) {
Slog.i(TAG, "Adding widget participants to backup set:");
@@ -7232,7 +7239,8 @@
// Used by both incremental and full restore
void restoreWidgetData(String packageName, byte[] widgetData) {
// Apply the restored widget state and generate the ID update for the app
- AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER);
+ // TODO: http://b/22388012
+ AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
}
// *****************************
@@ -7489,7 +7497,8 @@
// If we're starting a full-system restore, set up to begin widget ID remapping
if (mIsSystemRestore) {
- AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER);
+ // TODO: http://b/22388012
+ AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM);
}
try {
@@ -8095,7 +8104,8 @@
}
// Kick off any work that may be needed regarding app widget restores
- AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER);
+ // TODO: http://b/22388012
+ AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM);
// If this was a full-system restore, record the ancestral
// dataset information
@@ -8482,7 +8492,8 @@
public void dataChanged(final String packageName) {
final int callingUserHandle = UserHandle.getCallingUserId();
- if (callingUserHandle != UserHandle.USER_OWNER) {
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
+ // TODO: http://b/22388012
// App is running under a non-owner user profile. For now, we do not back
// up data from secondary user profiles.
// TODO: backups for all user profiles although don't add backup for profiles
@@ -8605,7 +8616,8 @@
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
final int callingUserHandle = UserHandle.getCallingUserId();
- if (callingUserHandle != UserHandle.USER_OWNER) {
+ // TODO: http://b/22388012
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
throw new IllegalStateException("Backup supported only for the device owner");
}
@@ -8677,7 +8689,8 @@
"fullTransportBackup");
final int callingUserHandle = UserHandle.getCallingUserId();
- if (callingUserHandle != UserHandle.USER_OWNER) {
+ // TODO: http://b/22388012
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
throw new IllegalStateException("Restore supported only for the device owner");
}
@@ -8717,7 +8730,8 @@
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
final int callingUserHandle = UserHandle.getCallingUserId();
- if (callingUserHandle != UserHandle.USER_OWNER) {
+ // TODO: http://b/22388012
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
throw new IllegalStateException("Restore supported only for the device owner");
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 5859c6a..a51ab55 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -62,7 +62,8 @@
// internal control API
public void initialize(final int whichUser) {
// Note that only the owner user is currently involved in backup/restore
- if (whichUser == UserHandle.USER_OWNER) {
+ // TODO: http://b/22388012
+ if (whichUser == UserHandle.USER_SYSTEM) {
// Does this product support backup/restore at all?
if (mGlobalDisable) {
Slog.i(TAG, "Backup/restore not supported");
@@ -91,8 +92,8 @@
Slog.i(TAG, "Backup/restore not supported");
return;
}
-
- if (userHandle == UserHandle.USER_OWNER) {
+ // TODO: http://b/22388012
+ if (userHandle == UserHandle.USER_SYSTEM) {
synchronized (this) {
if (makeActive != isBackupServiceActive(userHandle)) {
Slog.i(TAG, "Making backup "
@@ -120,7 +121,8 @@
* @return true if the service is active.
*/
public boolean isBackupServiceActive(final int userHandle) {
- if (userHandle == UserHandle.USER_OWNER) {
+ // TODO: http://b/22388012
+ if (userHandle == UserHandle.USER_SYSTEM) {
synchronized (this) {
return mService != null;
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d5c4a41..fd67b41 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -271,7 +271,7 @@
int sysUiUid = -1;
try {
sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui",
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
} catch (PackageManager.NameNotFoundException e) {
// Some platforms, such as wearables do not have a system ui.
Log.w(TAG, "Unable to resolve SystemUI's UID.", e);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d1e1683..a7e6f11 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -754,6 +754,8 @@
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTING);
intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+ intentFilter.addAction(Intent.ACTION_USER_ADDED);
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
@@ -2270,8 +2272,9 @@
mNetworkRequestInfoLogs.log("REGISTER " + nri);
if (!nri.isRequest) {
for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
- if (network.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(network);
+ if (nri.request.networkCapabilities.hasSignalStrength() &&
+ network.satisfiesImmutableCapabilitiesOf(nri.request)) {
+ updateSignalStrengthThresholds(network, "REGISTER", nri.request);
}
}
}
@@ -2388,8 +2391,9 @@
// if this listen request applies and remove it.
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
nai.networkRequests.remove(nri.request.requestId);
- if (nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(nai);
+ if (nri.request.networkCapabilities.hasSignalStrength() &&
+ nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+ updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
}
}
}
@@ -3523,6 +3527,26 @@
}
}
+ private void onUserAdded(int userId) {
+ synchronized(mVpns) {
+ final int vpnsSize = mVpns.size();
+ for (int i = 0; i < vpnsSize; i++) {
+ Vpn vpn = mVpns.valueAt(i);
+ vpn.onUserAdded(userId);
+ }
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ synchronized(mVpns) {
+ final int vpnsSize = mVpns.size();
+ for (int i = 0; i < vpnsSize; i++) {
+ Vpn vpn = mVpns.valueAt(i);
+ vpn.onUserRemoved(userId);
+ }
+ }
+ }
+
private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -3534,6 +3558,10 @@
onUserStart(userId);
} else if (Intent.ACTION_USER_STOPPING.equals(action)) {
onUserStop(userId);
+ } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+ onUserAdded(userId);
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ onUserRemoved(userId);
}
}
};
@@ -3639,9 +3667,24 @@
return new ArrayList<Integer>(thresholds);
}
- private void updateSignalStrengthThresholds(NetworkAgentInfo nai) {
+ private void updateSignalStrengthThresholds(
+ NetworkAgentInfo nai, String reason, NetworkRequest request) {
+ ArrayList<Integer> thresholdsArray = getSignalStrengthThresholds(nai);
Bundle thresholds = new Bundle();
- thresholds.putIntegerArrayList("thresholds", getSignalStrengthThresholds(nai));
+ thresholds.putIntegerArrayList("thresholds", thresholdsArray);
+
+ // TODO: Switch to VDBG.
+ if (DBG) {
+ String detail;
+ if (request != null && request.networkCapabilities.hasSignalStrength()) {
+ detail = reason + " " + request.networkCapabilities.getSignalStrength();
+ } else {
+ detail = reason;
+ }
+ log(String.format("updateSignalStrengthThresholds: %s, sending %s to %s",
+ detail, Arrays.toString(thresholdsArray.toArray()), nai.name()));
+ }
+
nai.asyncChannel.sendMessage(
android.net.NetworkAgent.CMD_SET_SIGNAL_STRENGTH_THRESHOLDS,
0, 0, thresholds);
@@ -4624,7 +4667,7 @@
// so we could decide to tear it down immediately afterwards. That's fine though - on
// disconnection NetworkAgents should stop any signal strength monitoring they have been
// doing.
- updateSignalStrengthThresholds(networkAgent);
+ updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
// Consider network even though it is not yet validated.
rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 46fd28a..ebcdf7c 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -31,6 +31,8 @@
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.hardware.display.DisplayManager;
@@ -111,7 +113,7 @@
private INetworkPolicyManager mNetworkPolicyManager;
private DisplayManager mDisplayManager;
private SensorManager mSensorManager;
- private Sensor mSigMotionSensor;
+ private Sensor mMotionSensor;
private LocationManager mLocationManager;
private LocationRequest mLocationRequest;
private PendingIntent mSensingAlarmIntent;
@@ -123,7 +125,6 @@
private boolean mForceIdle;
private boolean mScreenOn;
private boolean mCharging;
- private boolean mSigMotionActive;
private boolean mSensing;
private boolean mNotMoving;
private boolean mLocating;
@@ -268,13 +269,57 @@
}
};
- private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
- @Override public void onTrigger(TriggerEvent event) {
+ private final class MotionListener extends TriggerEventListener
+ implements SensorEventListener {
+
+ boolean active = false;
+
+ @Override
+ public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
- significantMotionLocked();
+ active = false;
+ motionLocked();
}
}
- };
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (DeviceIdleController.this) {
+ mSensorManager.unregisterListener(this, mMotionSensor);
+ active = false;
+ motionLocked();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ public boolean registerLocked() {
+ boolean success = false;
+ if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ success = mSensorManager.requestTriggerSensor(mMotionListener, mMotionSensor);
+ } else {
+ success = mSensorManager.registerListener(
+ mMotionListener, mMotionSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if (success) {
+ active = true;
+ } else {
+ Slog.e(TAG, "Unable to register for " + mMotionSensor);
+ }
+ return success;
+ }
+
+ public void unregisterLocked() {
+ if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ mSensorManager.cancelTriggerSensor(mMotionListener, mMotionSensor);
+ } else {
+ mSensorManager.unregisterListener(mMotionListener);
+ }
+ active = false;
+ }
+ }
+ private final MotionListener mMotionListener = new MotionListener();
private final LocationListener mGenericLocationListener = new LocationListener() {
@Override
@@ -349,7 +394,7 @@
* This is the time, after becoming inactive, at which we start looking at the
* motion sensor to determine if the device is being left alone. We don't do this
* immediately after going inactive just because we don't want to be continually running
- * the significant motion sensor whenever the screen is off.
+ * the motion sensor whenever the screen is off.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_INACTIVE_TIMEOUT
*/
@@ -392,7 +437,7 @@
/**
* This is the time, after the inactive timeout elapses, that we will wait looking
- * for significant motion until we truly consider the device to be idle.
+ * for motion until we truly consider the device to be idle.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
*/
@@ -886,18 +931,19 @@
int sigMotionSensorId = getContext().getResources().getInteger(
com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
if (sigMotionSensorId > 0) {
- mSigMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
+ mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
}
- if (mSigMotionSensor == null && getContext().getResources().getBoolean(
+ if (mMotionSensor == null && getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
- mSigMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_WRIST_TILT_GESTURE);
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_WRIST_TILT_GESTURE, true);
}
- if (mSigMotionSensor == null) {
+ if (mMotionSensor == null) {
// As a last ditch, fall back to SMD.
- mSigMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_SIGNIFICANT_MOTION);
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_SIGNIFICANT_MOTION, true);
}
+
if (getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
mLocationManager = (LocationManager) getContext().getSystemService(
@@ -1242,7 +1288,7 @@
cancelAlarmLocked();
cancelSensingAlarmLocked();
cancelLocatingLocked();
- stopMonitoringSignificantMotion();
+ stopMonitoringMotionLocked();
mAnyMotionDetector.stop();
}
@@ -1271,8 +1317,8 @@
switch (mState) {
case STATE_INACTIVE:
// We have now been inactive long enough, it is time to start looking
- // for significant motion and sleep some more while doing so.
- startMonitoringSignificantMotion();
+ // for motion and sleep some more while doing so.
+ startMonitoringMotionLocked();
scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
// Reset the upcoming idle delays.
mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
@@ -1353,17 +1399,16 @@
}
}
- void significantMotionLocked() {
- if (DEBUG) Slog.d(TAG, "significantMotionLocked()");
- // When the sensor goes off, its trigger is automatically removed.
- mSigMotionActive = false;
+ void motionLocked() {
+ if (DEBUG) Slog.d(TAG, "motionLocked()");
+ // The motion sensor will have been disabled at this point
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
}
void handleMotionDetectedLocked(long timeout, String type) {
// The device is not yet active, so we want to go back to the pending idle
- // state to wait again for no motion. Note that we only monitor for significant
- // motion after moving out of the inactive state, so no need to worry about that.
+ // state to wait again for no motion. Note that we only monitor for motion
+ // after moving out of the inactive state, so no need to worry about that.
if (mState != STATE_ACTIVE) {
scheduleReportActiveLocked(type, Process.myUid());
mState = STATE_ACTIVE;
@@ -1405,19 +1450,17 @@
}
}
- void startMonitoringSignificantMotion() {
- if (DEBUG) Slog.d(TAG, "startMonitoringSignificantMotion()");
- if (mSigMotionSensor != null && !mSigMotionActive) {
- mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
- mSigMotionActive = true;
+ void startMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "startMonitoringMotionLocked()");
+ if (mMotionSensor != null && !mMotionListener.active) {
+ mMotionListener.registerLocked();
}
}
- void stopMonitoringSignificantMotion() {
- if (DEBUG) Slog.d(TAG, "stopMonitoringSignificantMotion()");
- if (mSigMotionActive) {
- mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
- mSigMotionActive = false;
+ void stopMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
+ if (mMotionSensor != null && mMotionListener.active) {
+ mMotionListener.unregisterLocked();
}
}
@@ -1446,7 +1489,7 @@
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
- if (mSigMotionSensor == null) {
+ if (mMotionSensor == null) {
// If there is no motion sensor on this device, then we won't schedule
// alarms, because we can't determine if the device is not moving. This effectively
// turns off normal execution of device idling, although it is still possible to
@@ -1523,13 +1566,13 @@
private void reportPowerSaveWhitelistChangedLocked() {
Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- getContext().sendBroadcastAsUser(intent, UserHandle.OWNER);
+ getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM);
}
private void reportTempWhitelistChangedLocked() {
Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- getContext().sendBroadcastAsUser(intent, UserHandle.OWNER);
+ getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM);
}
void readConfigFileLocked() {
@@ -1689,7 +1732,7 @@
}
if (args != null) {
- int userId = UserHandle.USER_OWNER;
+ int userId = UserHandle.USER_SYSTEM;
for (int i=0; i<args.length; i++) {
String arg = args[i];
if ("-h".equals(arg)) {
@@ -1929,11 +1972,11 @@
pw.print(" mEnabled="); pw.println(mEnabled);
pw.print(" mForceIdle="); pw.println(mForceIdle);
- pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor);
+ pw.print(" mMotionSensor="); pw.println(mMotionSensor);
pw.print(" mCurDisplay="); pw.println(mCurDisplay);
pw.print(" mScreenOn="); pw.println(mScreenOn);
pw.print(" mCharging="); pw.println(mCharging);
- pw.print(" mSigMotionActive="); pw.println(mSigMotionActive);
+ pw.print(" mMotionActive="); pw.println(mMotionListener.active);
pw.print(" mSensing="); pw.print(mSensing); pw.print(" mNotMoving=");
pw.println(mNotMoving);
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index b9db89ec..2454487 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -170,7 +170,7 @@
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_SEND_BROADCAST) {
- getContext().sendBroadcastAsUser((Intent)msg.obj, UserHandle.OWNER,
+ getContext().sendBroadcastAsUser((Intent)msg.obj, UserHandle.SYSTEM,
android.Manifest.permission.READ_LOGS);
}
}
@@ -488,7 +488,7 @@
///////////////////////////////////////////////////////////////////////////
- /** Chronologically sorted list of {@link #EntryFile} */
+ /** Chronologically sorted list of {@link EntryFile} */
private static final class FileList implements Comparable<FileList> {
public int blocks = 0;
public final TreeSet<EntryFile> contents = new TreeSet<EntryFile>();
@@ -613,7 +613,7 @@
/**
* Creates a EntryFile object with only a timestamp for comparison purposes.
- * @param timestampMillis to compare with.
+ * @param millis to compare with.
*/
public EntryFile(long millis) {
this.tag = null;
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index b826cfd..9bf2aaa 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -29,6 +29,9 @@
# This is logged when the partial wake lock (keeping the device awake
# regardless of whether the screen is off) is acquired or released.
2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3)
+# The device is being asked to go into a soft sleep (typically by the ungaze gesture).
+# It logs the time remaining before the device would've normally gone to sleep without the request.
+2731 power_soft_sleep_requested (savedwaketimems|2)
#
# Leave IDs through 2739 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 342a3ef..f245985 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -17,7 +17,7 @@
package com.android.server;
import android.app.ActivityManager;
-import android.app.KeyguardManager;
+import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -33,10 +33,11 @@
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.Vibrator;
import android.provider.Settings;
import android.util.Slog;
+import android.view.KeyEvent;
+import com.android.internal.logging.MetricsLogger;
import com.android.server.statusbar.StatusBarManagerInternal;
/**
@@ -46,10 +47,16 @@
* added.</p>
* @hide
*/
-class GestureLauncherService extends SystemService {
+public class GestureLauncherService extends SystemService {
private static final boolean DBG = false;
private static final String TAG = "GestureLauncherService";
+ /**
+ * Time in milliseconds in which the power button must be pressed twice so it will be considered
+ * as a camera launch.
+ */
+ private static final long CAMERA_POWER_DOUBLE_TAP_TIME_MS = 300;
+
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -91,13 +98,19 @@
*/
private int mCameraLaunchLastEventExtra = 0;
+ /**
+ * Whether camera double tap power button gesture is currently enabled;
+ */
+ private boolean mCameraDoubleTapPowerEnabled;
+ private long mLastPowerDown;
+
public GestureLauncherService(Context context) {
super(context);
mContext = context;
}
public void onStart() {
- // Nothing to publish.
+ LocalServices.addService(GestureLauncherService.class, this);
}
public void onBootPhase(int phase) {
@@ -113,17 +126,21 @@
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"GestureLauncherService");
updateCameraRegistered();
+ updateCameraDoubleTapPowerEnabled();
mUserId = ActivityManager.getCurrentUser();
mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
- registerContentObserver();
+ registerContentObservers();
}
}
- private void registerContentObserver() {
+ private void registerContentObservers() {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
false, mSettingObserver, mUserId);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
+ false, mSettingObserver, mUserId);
}
private void updateCameraRegistered() {
@@ -135,6 +152,13 @@
}
}
+ private void updateCameraDoubleTapPowerEnabled() {
+ boolean enabled = isCameraDoubleTapPowerSettingEnabled(mContext, mUserId);
+ synchronized (this) {
+ mCameraDoubleTapPowerEnabled = enabled;
+ }
+ }
+
private void unregisterCameraLaunchGesture() {
if (mRegistered) {
mRegistered = false;
@@ -197,6 +221,12 @@
Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
}
+ public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) {
+ return isCameraDoubleTapPowerEnabled(context.getResources())
+ && (Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
+ }
+
/**
* Whether to enable the camera launch gesture.
*/
@@ -207,13 +237,68 @@
!SystemProperties.getBoolean("gesture.disable_camera_launch", false);
}
+ public static boolean isCameraDoubleTapPowerEnabled(Resources resources) {
+ return resources.getBoolean(
+ com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled);
+ }
+
/**
* Whether GestureLauncherService should be enabled according to system properties.
*/
public static boolean isGestureLauncherEnabled(Resources resources) {
- // For now, the only supported gesture is camera launch gesture, so whether to enable this
- // service equals to isCameraLaunchEnabled();
- return isCameraLaunchEnabled(resources);
+ return isCameraLaunchEnabled(resources) || isCameraDoubleTapPowerEnabled(resources);
+ }
+
+ public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive) {
+ boolean launched = false;
+ boolean intercept = false;
+ long doubleTapInterval;
+ synchronized (this) {
+ doubleTapInterval = event.getEventTime() - mLastPowerDown;
+ if (mCameraDoubleTapPowerEnabled
+ && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
+ launched = true;
+ intercept = interactive;
+ }
+ mLastPowerDown = event.getEventTime();
+ }
+ if (launched) {
+ Slog.i(TAG, "Power button double tap gesture detected, launching camera.");
+ launched = handleCameraLaunchGesture(false /* useWakelock */,
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
+ if (launched) {
+ MetricsLogger.action(mContext, MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
+ (int) doubleTapInterval);
+ }
+ }
+ MetricsLogger.histogram(mContext, "power_double_tap_interval", (int) doubleTapInterval);
+ return intercept && launched;
+ }
+
+ /**
+ * @return true if camera was launched, false otherwise.
+ */
+ private boolean handleCameraLaunchGesture(boolean useWakelock, int source) {
+ boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+ if (!userSetupComplete) {
+ if (DBG) Slog.d(TAG, String.format(
+ "userSetupComplete = %s, ignoring camera launch gesture.",
+ userSetupComplete));
+ return false;
+ }
+ if (DBG) Slog.d(TAG, String.format(
+ "userSetupComplete = %s, performing camera launch gesture.",
+ userSetupComplete));
+
+ if (useWakelock) {
+ // Make sure we don't sleep too early
+ mWakeLock.acquire(500L);
+ }
+ StatusBarManagerInternal service = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ service.onCameraLaunchGestureDetected(source);
+ return true;
}
private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
@@ -222,8 +307,9 @@
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
- registerContentObserver();
+ registerContentObservers();
updateCameraRegistered();
+ updateCameraDoubleTapPowerEnabled();
}
}
};
@@ -232,6 +318,7 @@
public void onChange(boolean selfChange, android.net.Uri uri, int userId) {
if (userId == mUserId) {
updateCameraRegistered();
+ updateCameraDoubleTapPowerEnabled();
}
}
};
@@ -244,38 +331,20 @@
return;
}
if (event.sensor == mCameraLaunchSensor) {
- handleCameraLaunchGesture(event);
+ if (DBG) {
+ float[] values = event.values;
+ Slog.d(TAG, String.format("Received a camera launch event: " +
+ "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
+ }
+ if (handleCameraLaunchGesture(true /* useWakelock */,
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)) {
+ MetricsLogger.action(mContext, MetricsLogger.ACTION_WIGGLE_CAMERA_GESTURE);
+ trackCameraLaunchEvent(event);
+ }
return;
}
}
- private void handleCameraLaunchGesture(SensorEvent event) {
- if (DBG) {
- float[] values = event.values;
- Slog.d(TAG, String.format("Received a camera launch event: " +
- "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
- }
- boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
- if (!userSetupComplete) {
- if (DBG) Slog.d(TAG, String.format(
- "userSetupComplete = %s, ignoring camera launch gesture.",
- userSetupComplete));
- return;
- }
- if (DBG) Slog.d(TAG, String.format(
- "userSetupComplete = %s, performing camera launch gesture.",
- userSetupComplete));
-
- // Make sure we don't sleep too early
- mWakeLock.acquire(500L);
- StatusBarManagerInternal service = LocalServices.getService(
- StatusBarManagerInternal.class);
- service.onCameraLaunchGestureDetected();
- trackCameraLaunchEvent(event);
- mWakeLock.release();
- }
-
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Ignored.
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index b418aba..9dad7a1 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -541,7 +541,8 @@
prevSubtypes.addAll(entry.getValue());
}
- final String mergedImesAndSubtypesString = buildInputMethodsAndSubtypesString(prevMap);
+ final String mergedImesAndSubtypesString =
+ InputMethodUtils.buildInputMethodsAndSubtypesString(prevMap);
if (DEBUG_RESTORE) {
Slog.i(TAG, "Merged IME string:");
Slog.i(TAG, " " + mergedImesAndSubtypesString);
@@ -550,23 +551,6 @@
Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString);
}
- // TODO: Move this method to InputMethodUtils with adding unit tests.
- static String buildInputMethodsAndSubtypesString(ArrayMap<String, ArraySet<String>> map) {
- // we want to use the canonical InputMethodSettings implementation,
- // so we convert data structures first.
- List<Pair<String, ArrayList<String>>> imeMap = new ArrayList<>(4);
- for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
- final String imeName = entry.getKey();
- final ArraySet<String> subtypeSet = entry.getValue();
- final ArrayList<String> subtypes = new ArrayList<>(2);
- if (subtypeSet != null) {
- subtypes.addAll(subtypeSet);
- }
- imeMap.add(new Pair<>(imeName, subtypes));
- }
- return InputMethodSettings.buildInputMethodsSettingString(imeMap);
- }
-
class MyPackageMonitor extends PackageMonitor {
private boolean isChangingPackagesOfCurrentUser() {
final int userId = getChangingUserId();
@@ -3504,7 +3488,7 @@
throw new NullPointerException("methodMap is null");
}
mMethodMap = methodMap;
- final File systemDir = userId == UserHandle.USER_OWNER
+ final File systemDir = userId == UserHandle.USER_SYSTEM
? new File(Environment.getDataDirectory(), SYSTEM_PATH)
: Environment.getUserSystemDirectory(userId);
final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 885c765..087ddd6 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -211,8 +211,8 @@
new ArrayList<LocationProviderProxy>();
// current active user on the device - other users are denied location data
- private int mCurrentUserId = UserHandle.USER_OWNER;
- private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_OWNER };
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
+ private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_SYSTEM };
public LocationManagerService(Context context) {
super();
@@ -1832,7 +1832,8 @@
// geo-fence manager uses the public location API, need to clear identity
int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != UserHandle.USER_OWNER) {
+ // TODO: http://b/23822629
+ if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) {
// temporary measure until geofences work for secondary users
Log.w(TAG, "proximity alerts are currently available only to the primary user");
return;
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index c7e0c98..da81528 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -145,7 +145,8 @@
} catch (RemoteException e) {
Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
}
- mStorage.prefetchUser(UserHandle.USER_OWNER);
+ // TODO: maybe skip this for split system user mode.
+ mStorage.prefetchUser(UserHandle.USER_SYSTEM);
}
private void migrateOldData() {
diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java
index c023f4a..0e4d5a7 100644
--- a/services/core/java/com/android/server/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java
@@ -132,7 +132,7 @@
}
public void requireStrongAuth(int strongAuthReason, int userId) {
- if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_OWNER) {
+ if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_SYSTEM) {
mHandler.obtainMessage(MSG_REQUIRE_STRONG_AUTH, strongAuthReason,
userId).sendToTarget();
} else {
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index e0352e0..33f9234 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -500,7 +500,7 @@
private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
int permission) {
final int callingUserId = UserHandle.getCallingUserId();
- if (callingUserId != UserHandle.USER_OWNER) {
+ if (callingUserId != UserHandle.USER_SYSTEM) {
contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
}
long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 72cece3..c3d32c2 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -370,11 +370,17 @@
private boolean shouldBenchmark() {
final long benchInterval = Settings.Global.getLong(mContext.getContentResolver(),
Settings.Global.STORAGE_BENCHMARK_INTERVAL, DateUtils.WEEK_IN_MILLIS);
+ if (benchInterval == -1) {
+ return false;
+ } else if (benchInterval == 0) {
+ return true;
+ }
+
synchronized (mLock) {
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
final VolumeRecord rec = mRecords.get(vol.fsUuid);
- if (vol.isMountedReadable() && rec != null) {
+ if (vol.isMountedWritable() && rec != null) {
final long benchAge = System.currentTimeMillis() - rec.lastBenchMillis;
if (benchAge >= benchInterval) {
return true;
@@ -2975,7 +2981,7 @@
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
- UserHandle.OWNER)) {
+ UserHandle.SYSTEM)) {
mBound = true;
return true;
}
@@ -3008,7 +3014,6 @@
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
handleError();
- return;
} else {
handleExecute();
if (DEBUG_OBB)
@@ -3170,8 +3175,6 @@
waitForReady();
warnOnNotMounted();
- final ObbInfo obbInfo = getObbInfo();
-
final ObbState existingState;
synchronized (mObbMounts) {
existingState = mObbPathToStateMap.get(mObbState.rawPath);
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index b05a690..b984e19 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -133,8 +133,8 @@
filter.addDataSchemeSpecificPart(scorer.mPackageName,
PatternMatcher.PATTERN_LITERAL);
mReceiver = new ScorerChangedReceiver(scorer.mPackageName);
- // TODO: Need to update when we support per-user scorers.
- mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter, null, null);
+ // TODO: Need to update when we support per-user scorers. http://b/23422763
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter, null, null);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Registered receiver for " + scorer.mPackageName);
}
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index aace66c..fc3a322 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -99,7 +99,7 @@
broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(new TextServicesBroadcastReceiver(), broadcastFilter);
- int userId = UserHandle.USER_OWNER;
+ int userId = UserHandle.USER_SYSTEM;
try {
ActivityManagerNative.getDefault().registerUserSwitchObserver(
new IUserSwitchObserver.Stub() {
diff --git a/services/core/java/com/android/server/ThermalObserver.java b/services/core/java/com/android/server/ThermalObserver.java
new file mode 100644
index 0000000..aee28fb
--- /dev/null
+++ b/services/core/java/com/android/server/ThermalObserver.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UEventObserver;
+import android.os.UserHandle;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * ThermalObserver for monitoring temperature changes.
+ */
+public class ThermalObserver extends SystemService {
+ private static final String TAG = "ThermalObserver";
+
+ private static final String CALLSTATE_UEVENT_MATCH =
+ "DEVPATH=/devices/virtual/switch/thermalstate";
+
+ private static final int MSG_THERMAL_STATE_CHANGED = 0;
+
+ private static final int SWITCH_STATE_NORMAL = 0;
+ private static final int SWITCH_STATE_WARNING = 1;
+ private static final int SWITCH_STATE_EXCEEDED = 2;
+
+ private final PowerManager mPowerManager;
+ private final PowerManager.WakeLock mWakeLock;
+
+ private final Object mLock = new Object();
+ private Integer mLastState;
+
+ private final UEventObserver mThermalWarningObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ updateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+ }
+ };
+
+ private final Handler mHandler = new Handler(true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_THERMAL_STATE_CHANGED:
+ handleThermalStateChange(msg.arg1);
+ mWakeLock.release();
+ break;
+ }
+ }
+ };
+
+ public ThermalObserver(Context context) {
+ super(context);
+ mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ mThermalWarningObserver.startObserving(CALLSTATE_UEVENT_MATCH);
+ }
+
+ private void updateLocked(int state) {
+ Message message = new Message();
+ message.what = MSG_THERMAL_STATE_CHANGED;
+ message.arg1 = state;
+
+ mWakeLock.acquire();
+ mHandler.sendMessage(message);
+ }
+
+ private void handleThermalStateChange(int state) {
+ synchronized (mLock) {
+ mLastState = state;
+ Intent intent = new Intent(Intent.ACTION_THERMAL_EVENT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+ final int thermalState;
+
+ switch (state) {
+ case SWITCH_STATE_WARNING:
+ thermalState = Intent.EXTRA_THERMAL_STATE_WARNING;
+ break;
+ case SWITCH_STATE_EXCEEDED:
+ thermalState = Intent.EXTRA_THERMAL_STATE_EXCEEDED;
+ break;
+ case SWITCH_STATE_NORMAL:
+ default:
+ thermalState = Intent.EXTRA_THERMAL_STATE_NORMAL;
+ break;
+ }
+
+ intent.putExtra(Intent.EXTRA_THERMAL_STATE, thermalState);
+
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(TAG, new BinderService());
+ }
+
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump thermal observer service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (args == null || args.length == 0 || "-a".equals(args[0])) {
+ pw.println("Current Thermal Observer Service state:");
+ pw.println(" last state change: "
+ + (mLastState != null ? mLastState : "none"));
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 30f4dce..c228422 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -59,6 +59,7 @@
implements InputManager.InputDeviceListener {
private static final String TAG = "VibratorService";
private static final boolean DEBUG = false;
+ private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
private final LinkedList<Vibration> mVibrations;
private final LinkedList<VibrationInfo> mPreviousVibrations;
@@ -147,7 +148,8 @@
}
public boolean isSystemHapticFeedback() {
- return (mUid == Process.SYSTEM_UID || mUid == 0) && mRepeat < 0;
+ return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg))
+ && mRepeat < 0;
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 7aef38d..6b34612 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -29,9 +29,11 @@
import android.accounts.IAccountAuthenticatorResponse;
import android.accounts.IAccountManager;
import android.accounts.IAccountManagerResponse;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -122,6 +124,7 @@
private final Context mContext;
private final PackageManager mPackageManager;
+ private final AppOpsManager mAppOpsManager;
private UserManager mUserManager;
private final MessageHandler mMessageHandler;
@@ -266,6 +269,7 @@
IAccountAuthenticatorCache authenticatorCache) {
mContext = context;
mPackageManager = packageManager;
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mMessageHandler = new MessageHandler(FgThread.get().getLooper());
@@ -510,7 +514,7 @@
// Check if there's a shared account that needs to be created as an account
Account[] sharedAccounts = getSharedAccountsAsUser(userId);
if (sharedAccounts == null || sharedAccounts.length == 0) return;
- Account[] accounts = getAccountsAsUser(null, userId);
+ Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
int parentUserId = UserManager.isSplitSystemUser()
? mUserManager.getUserInfo(userId).restrictedProfileParentId
: UserHandle.USER_SYSTEM;
@@ -876,7 +880,8 @@
// Confirm that the owner's account still exists before this step.
UserAccounts owner = getUserAccounts(parentUserId);
synchronized (owner.cacheLock) {
- for (Account acc : getAccounts(parentUserId)) {
+ for (Account acc : getAccounts(parentUserId,
+ mContext.getOpPackageName())) {
if (acc.equals(account)) {
mAuthenticator.addAccountFromCredentials(
this, account, accountCredentials);
@@ -996,7 +1001,7 @@
@Override
public void hasFeatures(IAccountManagerResponse response,
- Account account, String[] features) {
+ Account account, String[] features, String opPackageName) {
int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "hasFeatures: " + account
@@ -1009,7 +1014,8 @@
if (account == null) throw new IllegalArgumentException("account is null");
if (features == null) throw new IllegalArgumentException("features is null");
int userId = UserHandle.getCallingUserId();
- checkReadAccountsPermitted(callingUid, account.type, userId);
+ checkReadAccountsPermitted(callingUid, account.type, userId,
+ opPackageName);
long identityToken = clearCallingIdentity();
try {
@@ -2521,9 +2527,11 @@
* Returns the accounts visible to the client within the context of a specific user
* @hide
*/
- public Account[] getAccounts(int userId) {
+ @NonNull
+ public Account[] getAccounts(int userId, String opPackageName) {
int callingUid = Binder.getCallingUid();
- List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId);
+ List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
+ opPackageName);
if (visibleAccountTypes.isEmpty()) {
return new Account[0];
}
@@ -2545,6 +2553,7 @@
*
* @hide
*/
+ @NonNull
public AccountAndUser[] getRunningAccounts() {
final int[] runningUserIds;
try {
@@ -2557,6 +2566,7 @@
}
/** {@hide} */
+ @NonNull
public AccountAndUser[] getAllAccounts() {
final List<UserInfo> users = getUserManager().getUsers();
final int[] userIds = new int[users.size()];
@@ -2566,6 +2576,7 @@
return getAccounts(userIds);
}
+ @NonNull
private AccountAndUser[] getAccounts(int[] userIds) {
final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
for (int userId : userIds) {
@@ -2585,15 +2596,18 @@
}
@Override
- public Account[] getAccountsAsUser(String type, int userId) {
- return getAccountsAsUser(type, userId, null, -1);
+ @NonNull
+ public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
+ return getAccountsAsUser(type, userId, null, -1, opPackageName);
}
+ @NonNull
private Account[] getAccountsAsUser(
String type,
int userId,
String callingPackage,
- int packageUid) {
+ int packageUid,
+ String opPackageName) {
int callingUid = Binder.getCallingUid();
// Only allow the system process to read accounts of other users
if (userId != UserHandle.getCallingUserId()
@@ -2614,9 +2628,11 @@
// be passed in the original caller's uid here, which is what should be used for filtering.
if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
callingUid = packageUid;
+ opPackageName = callingPackage;
}
- List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId);
+ List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
+ opPackageName);
if (visibleAccountTypes.isEmpty()
|| (type != null && !visibleAccountTypes.contains(type))) {
return new Account[0];
@@ -2640,6 +2656,7 @@
}
}
+ @NonNull
private Account[] getAccountsInternal(
UserAccounts userAccounts,
int callingUid,
@@ -2663,7 +2680,15 @@
}
@Override
- public boolean addSharedAccountAsUser(Account account, int userId) {
+ public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
+ checkManageUsersPermission("addSharedAccountsFromParentUser");
+ Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
+ for (Account account : accounts) {
+ addSharedAccountAsUser(account, userId);
+ }
+ }
+
+ private boolean addSharedAccountAsUser(Account account, int userId) {
userId = handleIncomingUser(userId);
UserAccounts accounts = getUserAccounts(userId);
SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
@@ -2755,22 +2780,27 @@
}
@Override
- public Account[] getAccounts(String type) {
- return getAccountsAsUser(type, UserHandle.getCallingUserId());
+ @NonNull
+ public Account[] getAccounts(String type, String opPackageName) {
+ return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
}
@Override
- public Account[] getAccountsForPackage(String packageName, int uid) {
+ @NonNull
+ public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
int callingUid = Binder.getCallingUid();
if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
+ callingUid + " with uid=" + uid);
}
- return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid);
+ return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
+ opPackageName);
}
@Override
- public Account[] getAccountsByTypeForPackage(String type, String packageName) {
+ @NonNull
+ public Account[] getAccountsByTypeForPackage(String type, String packageName,
+ String opPackageName) {
int packageUid = -1;
try {
packageUid = AppGlobals.getPackageManager().getPackageUid(
@@ -2779,14 +2809,16 @@
Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
return new Account[0];
}
- return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, packageUid);
+ return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
+ packageUid, opPackageName);
}
@Override
public void getAccountsByFeatures(
IAccountManagerResponse response,
String type,
- String[] features) {
+ String[] features,
+ String opPackageName) {
int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getAccounts: accountType " + type
@@ -2799,7 +2831,8 @@
if (type == null) throw new IllegalArgumentException("accountType is null");
int userId = UserHandle.getCallingUserId();
- List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId);
+ List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
+ opPackageName);
if (!visibleAccountTypes.contains(type)) {
Bundle result = new Bundle();
// Need to return just the accounts that are from matching signatures.
@@ -3699,31 +3732,22 @@
}
}
- private boolean isPermitted(int callingUid, String... permissions) {
+ private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
for (String perm : permissions) {
if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, " caller uid " + callingUid + " has " + perm);
}
- return true;
+ final int opCode = AppOpsManager.permissionToOpCode(perm);
+ if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
+ opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
}
}
return false;
}
- /** Succeeds if any of the specified permissions are granted. */
- private void checkBinderPermission(String... permissions) {
- final int callingUid = Binder.getCallingUid();
- if (isPermitted(callingUid, permissions)) {
- String msg = String.format(
- "caller uid %s lacks any of %s",
- callingUid,
- TextUtils.join(",", permissions));
- Log.w(TAG, " " + msg);
- throw new SecurityException(msg);
- }
- }
-
private int handleIncomingUser(int userId) {
try {
return ActivityManagerNative.getDefault().handleIncomingUser(
@@ -3777,11 +3801,13 @@
return fromAuthenticator || hasExplicitGrants || isPrivileged;
}
- private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId) {
+ private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
+ String opPackageName) {
if (accountType == null) {
return false;
} else {
- return getTypesVisibleToCaller(callingUid, userId).contains(accountType);
+ return getTypesVisibleToCaller(callingUid, userId,
+ opPackageName).contains(accountType);
}
}
@@ -3793,9 +3819,10 @@
}
}
- private List<String> getTypesVisibleToCaller(int callingUid, int userId) {
+ private List<String> getTypesVisibleToCaller(int callingUid, int userId,
+ String opPackageName) {
boolean isPermitted =
- isPermitted(callingUid, Manifest.permission.GET_ACCOUNTS,
+ isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Log.i(TAG, String.format("getTypesVisibleToCaller: isPermitted? %s", isPermitted));
return getTypesForCaller(callingUid, userId, isPermitted);
@@ -3836,6 +3863,14 @@
return false;
}
+ private static void checkManageUsersPermission(String message) {
+ if (ActivityManager.checkComponentPermission(
+ android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need MANAGE_USERS permission to: " + message);
+ }
+ }
+
private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
int callerUid) {
if (callerUid == Process.SYSTEM_UID) {
@@ -3891,8 +3926,9 @@
private void checkReadAccountsPermitted(
int callingUid,
String accountType,
- int userId) {
- if (!isAccountVisibleToCaller(accountType, callingUid, userId)) {
+ int userId,
+ String opPackageName) {
+ if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
String msg = String.format(
"caller uid %s cannot access %s accounts",
callingUid,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 81936ee..bd10c63 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19,7 +19,6 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.DOCKED_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
import static android.app.ActivityManager.INVALID_STACK_ID;
@@ -34,6 +33,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -215,7 +215,6 @@
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.Trace;
import android.os.UpdateLock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -323,6 +322,9 @@
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000;
+ // How long we wait for an attached process to publish its content providers
+ // before we decide it must be hung.
+ static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real, when the process was
@@ -992,6 +994,8 @@
*/
int mConfigurationSeq = 0;
+ boolean mSuppressResizeConfigChanges = false;
+
/**
* Hardware-reported OpenGLES version.
*/
@@ -1380,6 +1384,7 @@
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 56;
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 57;
static final int APP_BOOST_DEACTIVATE_MSG = 58;
+ static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 59;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1716,6 +1721,12 @@
processStartTimedOutLocked(app);
}
} break;
+ case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
+ ProcessRecord app = (ProcessRecord)msg.obj;
+ synchronized (ActivityManagerService.this) {
+ processContentProviderPublishTimedOutLocked(app);
+ }
+ } break;
case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
synchronized (ActivityManagerService.this) {
mStackSupervisor.doPendingActivityLaunchesLocked(true);
@@ -2012,7 +2023,7 @@
intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
new UserHandle(userId)))
.setDeleteIntent(PendingIntent.getBroadcastAsUser(mContext, 0,
- deleteIntent, 0, UserHandle.OWNER))
+ deleteIntent, 0, UserHandle.SYSTEM))
.build();
try {
@@ -2370,8 +2381,8 @@
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
// User 0 is the first and only user that runs at boot.
- mStartedUsers.put(UserHandle.USER_OWNER, new UserState(UserHandle.OWNER, true));
- mUserLru.add(UserHandle.USER_OWNER);
+ mStartedUsers.put(UserHandle.USER_SYSTEM, new UserState(UserHandle.SYSTEM, true));
+ mUserLru.add(UserHandle.USER_SYSTEM);
updateStartedUserArrayLocked();
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
@@ -2730,31 +2741,17 @@
}
}
- /**
- * Activate an activity by bringing it to the top and set the focus on it.
- * Note: This is only allowed for activities which are on the freeform stack.
- * @param token The token of the activity calling which will get activated.
- */
@Override
- public void activateActivity(IBinder token) {
- if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "ActivateActivity token=" + token);
+ public void setFocusedTask(int taskId) {
+ if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
long callingId = Binder.clearCallingIdentity();
try {
synchronized (ActivityManagerService.this) {
- final ActivityRecord anyTaskRecord = ActivityRecord.isInStackLocked(token);
- if (anyTaskRecord == null) {
- Slog.w(TAG, "ActivateActivity: token=" + token + " not found");
- return;
- }
- TaskRecord task = anyTaskRecord.task;
- final boolean runsOnFreeformStack =
- task.stack.getStackId() == FREEFORM_WORKSPACE_STACK_ID;
- if (!runsOnFreeformStack) {
- Slog.w(TAG, "Tried to use activateActivity on a non freeform workspace!");
- } else if (task != null) {
- ActivityRecord topTaskRecord = task.topRunningActivityLocked(null);
- if (topTaskRecord != null) {
- setFocusedActivityLocked(topTaskRecord, "activateActivity");
+ TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task != null) {
+ ActivityRecord r = task.topRunningActivityLocked(null);
+ if (r != null) {
+ setFocusedActivityLocked(r, "setFocusedTask");
mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
}
}
@@ -2764,21 +2761,6 @@
}
}
- @Override
- public void setFocusedTask(int taskId) {
- if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
- synchronized (ActivityManagerService.this) {
- TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
- if (task != null) {
- ActivityRecord r = task.topRunningActivityLocked(null);
- if (r != null) {
- setFocusedActivityLocked(r, "setFocusedTask");
- mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
- }
- }
- }
- }
-
/** Sets the task stack listener that gets callbacks when a task stack changes. */
@Override
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
@@ -4647,7 +4629,7 @@
// is hosted by the process... then make sure all visible
// activities are running, taking care of restarting this
// process.
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
}
@@ -5667,7 +5649,7 @@
try {
// Entire package setting changed
enabled = pm.getApplicationEnabledSetting(packageName,
- (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_OWNER);
+ (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_SYSTEM);
} catch (Exception e) {
// No such package/component; probably racing with uninstall. In any
// event it means we have nothing further to do here.
@@ -5685,7 +5667,7 @@
try {
enabled = pm.getComponentEnabledSetting(
new ComponentName(packageName, changedClass),
- (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_OWNER);
+ (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_SYSTEM);
} catch (Exception e) {
// As above, probably racing with uninstall.
return;
@@ -5993,6 +5975,11 @@
return needRestart;
}
+ private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
+ cleanupAppInLaunchingProvidersLocked(app, true);
+ removeProcessLocked(app, false, true, "timeout publishing content providers");
+ }
+
private final void processStartTimedOutLocked(ProcessRecord app) {
final int pid = app.pid;
boolean gone = false;
@@ -6019,7 +6006,7 @@
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
// Take care of any launching providers waiting for this process.
- checkAppInLaunchingProvidersLocked(app, true);
+ cleanupAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
mServices.processStartTimedOutLocked(app);
app.kill("start timeout", true);
@@ -6115,6 +6102,12 @@
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
+ if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
+ Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
+ msg.obj = app;
+ mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
+ }
+
if (!normalMode) {
Slog.i(TAG, "Launching preboot mode app: " + app);
}
@@ -8674,14 +8667,14 @@
}
if (task.mResizeable != resizeable) {
task.mResizeable = resizeable;
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mStackSupervisor.resumeTopActivitiesLocked();
}
}
}
@Override
- public void resizeTask(int taskId, Rect bounds) {
+ public void resizeTask(int taskId, Rect bounds, int resizeMode) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"resizeTask()");
long ident = Binder.clearCallingIdentity();
@@ -8692,7 +8685,7 @@
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return;
}
- mStackSupervisor.resizeTaskLocked(task, bounds);
+ mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -8721,58 +8714,6 @@
}
@Override
- public void setActivityBounds(IBinder token, Rect bounds) {
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- Slog.w(TAG, "setActivityBounds: token=" + token + " not found");
- return;
- }
- final TaskRecord task = r.task;
- if (task == null) {
- Slog.e(TAG, "setActivityBounds: No TaskRecord for the ActivityRecord r=" + r);
- return;
- }
- if (task.stack != null && task.stack.mStackId == DOCKED_STACK_ID) {
- mStackSupervisor.resizeStackLocked(task.stack.mStackId, bounds);
- } else {
- mStackSupervisor.resizeTaskLocked(task, bounds);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public Rect getActivityBounds(IBinder token) {
- long ident = Binder.clearCallingIdentity();
- Rect rect = null;
- try {
- synchronized (this) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- Slog.w(TAG, "getActivityBounds: token=" + token + " not found");
- return rect;
- }
- final TaskRecord task = r.task;
- if (task == null) {
- Slog.e(TAG, "getActivityBounds: No TaskRecord for the ActivityRecord r=" + r);
- return rect;
- }
- if (task.mBounds != null) {
- rect = new Rect(task.mBounds);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- return rect;
- }
-
- @Override
public Bitmap getTaskDescriptionIcon(String filename) {
if (!FileUtils.isValidExtFilename(filename)
|| !filename.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
@@ -9134,6 +9075,35 @@
}
}
+ /**
+ * Moves the input task to the docked stack.
+ *
+ * @param taskId Id of task to move.
+ * @param createMode The mode the docked stack should be created in if it doesn't exist
+ * already. See
+ * {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT}
+ * and
+ * {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT}
+ * @param toTop If the task and stack should be moved to the top.
+ */
+ @Override
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "moveTaskToDockedStack()");
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ + " to createMode=" + createMode + " toTop=" + toTop);
+ mWindowManager.setDockedStackCreateMode(createMode);
+ mStackSupervisor.moveTaskToStackLocked(
+ taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
@Override
public void resizeStack(int stackId, Rect bounds) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
@@ -9141,7 +9111,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- mStackSupervisor.resizeStackLocked(stackId, bounds);
+ mStackSupervisor.resizeStackLocked(stackId, bounds, !PRESERVE_WINDOWS);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -9424,7 +9394,7 @@
(ProviderInfo)providers.get(i);
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags);
- if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_OWNER) {
+ if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
// This is a singleton provider, but a user besides the
// default user is asking to initialize a process it runs
// in... well, no, it doesn't actually run in this process,
@@ -9646,7 +9616,7 @@
}
}
- private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
+ private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
@@ -9674,14 +9644,14 @@
cpr = mProviderMap.getProviderByName(name, userId);
// If that didn't work, check if it exists for user 0 and then
// verify that it's a singleton provider before using it.
- if (cpr == null && userId != UserHandle.USER_OWNER) {
- cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER);
+ if (cpr == null && userId != UserHandle.USER_SYSTEM) {
+ cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
if (isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
- userId = UserHandle.USER_OWNER;
+ userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
cpr = null;
@@ -9774,7 +9744,6 @@
Binder.restoreCallingIdentity(origId);
}
- boolean singleton;
if (!providerRunning) {
try {
checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
@@ -9791,11 +9760,11 @@
// (it's a call within the same user || the provider is a
// privileged app)
// Then allow connecting to the singleton provider
- singleton = isSingleton(cpi.processName, cpi.applicationInfo,
+ boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
if (singleton) {
- userId = UserHandle.USER_OWNER;
+ userId = UserHandle.USER_SYSTEM;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
checkTime(startTime, "getContentProviderImpl: got app info for user");
@@ -10103,7 +10072,7 @@
final long origId = Binder.clearCallingIdentity();
final int N = providers.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
@@ -10118,15 +10087,20 @@
mProviderMap.putProviderByName(names[j], dst);
}
- int NL = mLaunchingProviders.size();
+ int launchingCount = mLaunchingProviders.size();
int j;
- for (j=0; j<NL; j++) {
+ boolean wasInLaunchingProviders = false;
+ for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
+ wasInLaunchingProviders = true;
j--;
- NL--;
+ launchingCount--;
}
}
+ if (wasInLaunchingProviders) {
+ mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
+ }
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
@@ -10393,7 +10367,7 @@
}
final ProcessRecord r = new ProcessRecord(stats, info, proc, uid);
if (!mBooted && !mBooting
- && userId == UserHandle.USER_OWNER
+ && userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
r.persistent = true;
}
@@ -11216,7 +11190,7 @@
final boolean translucentChanged = r.changeWindowTranslucency(true);
if (translucentChanged) {
r.task.stack.releaseBackgroundResources(r);
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
mWindowManager.setAppFullscreen(token, true);
return translucentChanged;
@@ -11244,7 +11218,7 @@
if (translucentChanged) {
r.task.stack.convertActivityToTranslucent(r);
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mWindowManager.setAppFullscreen(token, false);
return translucentChanged;
}
@@ -11850,12 +11824,12 @@
}
private boolean deliverPreBootCompleted(final Runnable onFinishCallback,
- ArrayList<ComponentName> doneReceivers, int userId) {
+ ArrayList<ComponentName> doneReceivers) {
Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
List<ResolveInfo> ris = null;
try {
ris = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, null, 0, userId);
+ intent, null, 0, UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
}
if (ris == null) {
@@ -11869,22 +11843,18 @@
}
intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
- // For User 0, load the version number. When delivering to a new user, deliver
- // to all receivers.
- if (userId == UserHandle.USER_OWNER) {
- ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
- for (int i=0; i<ris.size(); i++) {
- ActivityInfo ai = ris.get(i).activityInfo;
- ComponentName comp = new ComponentName(ai.packageName, ai.name);
- if (lastDoneReceivers.contains(comp)) {
- // We already did the pre boot receiver for this app with the current
- // platform version, so don't do it again...
- ris.remove(i);
- i--;
- // ...however, do keep it as one that has been done, so we don't
- // forget about it when rewriting the file of last done receivers.
- doneReceivers.add(comp);
- }
+ ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
+ for (int i=0; i<ris.size(); i++) {
+ ActivityInfo ai = ris.get(i).activityInfo;
+ ComponentName comp = new ComponentName(ai.packageName, ai.name);
+ if (lastDoneReceivers.contains(comp)) {
+ // We already did the pre boot receiver for this app with the current
+ // platform version, so don't do it again...
+ ris.remove(i);
+ i--;
+ // ...however, do keep it as one that has been done, so we don't
+ // forget about it when rewriting the file of last done receivers.
+ doneReceivers.add(comp);
}
}
@@ -11892,9 +11862,8 @@
return false;
}
- // If primary user, send broadcast to all available users, else just to userId
- final int[] users = userId == UserHandle.USER_OWNER ? getUsersLocked()
- : new int[] { userId };
+ // TODO: can we still do this with per user encryption?
+ final int[] users = getUsersLocked();
if (users.length <= 0) {
return false;
}
@@ -11945,7 +11914,7 @@
writeLastDonePreBootReceivers(doneReceivers);
systemReady(goingCallback);
}
- }, doneReceivers, UserHandle.USER_OWNER);
+ }, doneReceivers);
if (mWaitingUpdate) {
return;
@@ -15797,7 +15766,7 @@
app.pubProviders.clear();
// Take care of any launching providers waiting for this process.
- if (checkAppInLaunchingProvidersLocked(app, false)) {
+ if (cleanupAppInLaunchingProvidersLocked(app, false)) {
restart = true;
}
@@ -15919,7 +15888,17 @@
return false;
}
- boolean checkAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
+ boolean checkAppInLaunchingProvidersLocked(ProcessRecord app) {
+ for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
+ ContentProviderRecord cpr = mLaunchingProviders.get(i);
+ if (cpr.launchingApp == app) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
// Look through the content providers we are waiting to have launched,
// and if any run in this process then either schedule a restart of
// the process or kill the client waiting for it if this process has
@@ -17538,6 +17517,15 @@
}
@Override
+ public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "suppressResizeConfigChanges()");
+ synchronized (this) {
+ mSuppressResizeConfigChanges = suppress;
+ }
+ }
+
+ @Override
public void updatePersistentConfiguration(Configuration values) {
enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
@@ -17714,10 +17702,11 @@
}
if (starting != null) {
- kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
+ kept = mainStack.ensureActivityConfigurationLocked(starting, changes, false);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
- mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
+ mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+ !PRESERVE_WINDOWS);
}
}
@@ -20188,7 +20177,7 @@
}
if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
- if (userId != UserHandle.USER_OWNER) {
+ if (userId != UserHandle.USER_SYSTEM) {
Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntentLocked(null, null, intent, null,
@@ -20426,7 +20415,7 @@
for (int i = 0; i < num; i++) {
Integer oldUserId = mUserLru.get(i);
UserState oldUss = mStartedUsers.get(oldUserId);
- if (oldUserId == UserHandle.USER_OWNER || oldUserId == mCurrentUserId
+ if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId
|| oldUss.mState == UserState.STATE_STOPPING
|| oldUss.mState == UserState.STATE_SHUTDOWN) {
continue;
@@ -20511,8 +20500,12 @@
i++;
continue;
}
- if (oldUserId == UserHandle.USER_OWNER || oldUserId == mCurrentUserId) {
- // Owner and current can't be stopped, but count as running.
+ if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) {
+ // Owner/System user and current user can't be stopped. We count it as running
+ // when it is not a pure system user.
+ if (UserInfo.isSystemOnly(oldUserId)) {
+ num--;
+ }
i++;
continue;
}
@@ -20535,8 +20528,8 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- if (userId < 0 || userId == UserHandle.USER_OWNER) {
- throw new IllegalArgumentException("Can't stop primary user " + userId);
+ if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
+ throw new IllegalArgumentException("Can't stop system user " + userId);
}
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
synchronized (this) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 751acb6..a796ea7 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -17,8 +17,11 @@
package com.android.server.am;
import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FIRST_STATIC_STACK_ID;
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
+import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -27,10 +30,11 @@
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.MOVING;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import android.graphics.Rect;
import android.util.ArraySet;
-import android.view.IApplicationToken;
+
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.BatteryStatsImpl;
@@ -525,21 +529,15 @@
* */
void moveToFront(String reason, TaskRecord task) {
if (isAttached()) {
- final boolean homeStack = isHomeStack()
- || (mActivityContainer.mParentActivity != null
- && mActivityContainer.mParentActivity.isHomeActivity());
- ActivityStack lastFocusStack = null;
- if (!homeStack) {
- // Need to move this stack to the front before calling
- // {@link ActivityStackSupervisor#moveHomeStack} below.
- lastFocusStack = mStacks.get(mStacks.size() - 1);
- mStacks.remove(this);
- mStacks.add(this);
- }
- // TODO(multi-display): Focus stack currently adjusted in call to move home stack.
- // Needs to also work if focus is moving to the non-home display.
+ final ActivityStack lastFocusStack = mStacks.get(mStacks.size() - 1);
+ // Need to move this stack to the front before calling
+ // {@link ActivityStackSupervisor#setFocusStack} below.
+ mStacks.remove(this);
+ mStacks.add(this);
+
+ // TODO(multi-display): Needs to also work if focus is moving to the non-home display.
if (isOnHomeDisplay()) {
- mStackSupervisor.moveHomeStack(homeStack, reason, lastFocusStack);
+ mStackSupervisor.setFocusStack(reason, lastFocusStack);
}
if (task != null) {
insertTaskAtTop(task, null);
@@ -812,7 +810,7 @@
}
void goToSleep() {
- ensureActivitiesVisibleLocked(null, 0);
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
// Make sure any stopped but visible activities are now sleeping.
// This ensures that the activity's onStop() is called.
@@ -1278,28 +1276,27 @@
return false;
}
- /** Return true if this stack is hidden by the presence of a docked stack. */
- private boolean isHiddenByDockedStack() {
- final ActivityStack dockedStack = mStackSupervisor.getStack(DOCKED_STACK_ID);
- if (dockedStack != null) {
- final int dockedStackIndex = mStacks.indexOf(dockedStack);
- final int stackIndex = mStacks.indexOf(this);
- if (dockedStackIndex > stackIndex) {
- // Fullscreen stacks or stacks with fullscreen task below the docked stack are not
- // visible. We do this so we don't have the 2 stacks and their tasks overlap.
- if (mFullscreen) {
- return true;
- }
+ private boolean hasTranslucentActivity(ActivityStack stack) {
+ final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = tasks.get(taskNdx);
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
- // We need to also check the tasks in the stack because they can be fullscreen
- // even though their stack isn't due to their root activity not been resizeable
- // (i.e. doesn't support multi-window mode).
- if (hasFullscreenTask()) {
- return true;
+ // Conditions for an activity to obscure the stack we're
+ // examining:
+ // 1. Not Finishing AND Visible AND:
+ // 2. Either:
+ // - Full Screen Activity OR
+ // - On top of Home and our stack is NOT home
+ if (!r.finishing && r.visible && (r.fullscreen ||
+ (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {
+ return false;
}
}
}
- return false;
+ return true;
}
/** Returns true if the stack is considered visible. */
@@ -1320,54 +1317,64 @@
return false;
}
- if (isHiddenByDockedStack()) {
+ final ActivityStack focusedStack = mStackSupervisor.getFocusedStack();
+ final int focusedStackId = focusedStack.mStackId;
+
+ if (mStackId == DOCKED_STACK_ID) {
+ // Docked stack is always visible, except in the case where the home activity
+ // is the top running activity in the focused home stack.
+ if (focusedStackId != HOME_STACK_ID) {
+ return true;
+ }
+ ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked(null);
+ return topHomeActivity == null || !topHomeActivity.isHomeActivity();
+ }
+
+ final int belowFocusedIndex = mStacks.indexOf(focusedStack) - 1;
+ if (focusedStackId == DOCKED_STACK_ID && stackIndex == belowFocusedIndex) {
+ // Stacks directly behind the docked stack are always visible.
+ return true;
+ }
+
+ if (focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
+ && hasTranslucentActivity(focusedStack)) {
+ // Stacks behind the fullscreen stack with a translucent activity are always
+ // visible so they can act as a backdrop to the translucent activity.
+ // For example, dialog activities
+ if (stackIndex == belowFocusedIndex) {
+ return true;
+ }
+ if (belowFocusedIndex >= 0) {
+ final ActivityStack stack = mStacks.get(belowFocusedIndex);
+ if (stack.mStackId == DOCKED_STACK_ID && stackIndex == (belowFocusedIndex - 1)) {
+ // The stack behind the docked stack is also visible so we can have a complete
+ // backdrop to the translucent activity when the docked stack is up.
+ return true;
+ }
+ }
+ }
+
+ if (mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID) {
+ // Visibility of any static stack should have been determined by the conditions above.
return false;
}
- /**
- * Start at the task above this one and go up, looking for a visible
- * fullscreen activity, or a translucent activity that requested the
- * wallpaper to be shown behind it.
- */
for (int i = stackIndex + 1; i < mStacks.size(); i++) {
final ActivityStack stack = mStacks.get(i);
- final ArrayList<TaskRecord> tasks = stack.getAllTasks();
if (!stack.mFullscreen && !stack.hasFullscreenTask()) {
continue;
}
if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID
- || stack.mStackId == HOME_STACK_ID) {
- // The freeform and home stacks can't have any other stack visible behind them
- // when they are fullscreen since they act as base/cut-off points for visibility.
- // NOTE: we don't cut-off at the FULLSCREEN_WORKSPACE_STACK_ID because the home
- // stack sometimes needs to be visible behind it when it is displaying a dialog
- // activity. We let it fall through to the logic below to determine visibility.
+ || stack.mStackId == HOME_STACK_ID
+ || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ // These stacks can't have any dynamic stacks visible behind them.
return false;
}
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- final TaskRecord task = tasks.get(taskNdx);
- // task above isn't fullscreen, so, we assume we're still visible.
- if (!task.mFullscreen) {
- continue;
- }
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
-
- // Conditions for an activity to obscure the stack we're
- // examining:
- // 1. Not Finishing AND Visible AND:
- // 2. Either:
- // - Full Screen Activity OR
- // - On top of Home and our stack is NOT home
- if (!r.finishing && r.visible && (r.fullscreen ||
- (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {
- return false;
- }
- }
+ if (!hasTranslucentActivity(stack)) {
+ return false;
}
}
@@ -1378,7 +1385,8 @@
* Make sure that all activities that need to be visible (that is, they
* currently can be seen by the user) actually are.
*/
- final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
+ final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+ boolean preserveWindows) {
ActivityRecord top = topRunningActivityLocked(null);
if (top == null) {
return;
@@ -1400,16 +1408,14 @@
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
boolean aboveTop = true;
- boolean behindFullscreen = !isStackVisibleLocked();
+ final boolean stackInvisible = !isStackVisibleLocked();
+ boolean behindFullscreenActivity = stackInvisible;
boolean noStackActivityResumed = (isInStackLocked(starting) == null);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
- // Set to true if an activity in this task is fullscreen thereby hiding other
- // activities in the same task. Initialized to the same value as behindFullscreen
- // which represent if the entire task/stack is behind another fullscreen task/stack.
- boolean behindFullscreenActivity = behindFullscreen;
+
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.finishing) {
@@ -1429,7 +1435,7 @@
// First: if this is not the current activity being started, make
// sure it matches the current configuration.
if (r != starting) {
- ensureActivityConfigurationLocked(r, 0);
+ ensureActivityConfigurationLocked(r, 0, preserveWindows);
}
if (r.app == null || r.app.thread == null) {
@@ -1507,18 +1513,18 @@
// At this point, nothing else needs to be shown in this task.
behindFullscreenActivity = true;
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
- + " behindFullscreen=" + behindFullscreen
+ + " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
} else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
behindFullscreenActivity = true;
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r
- + " behindFullscreen=" + behindFullscreen
+ + " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
}
} else {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Make invisible? " + r + " finishing=" + r.finishing
- + " state=" + r.state + " behindFullscreen=" + behindFullscreen
+ + " state=" + r.state + " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
// Now for any activities that aren't visible to the user, make
// sure they no longer are keeping the screen frozen.
@@ -1566,9 +1572,12 @@
}
}
}
- // Factoring if the previous task is fullscreen there by affecting the visibility of
- // task behind it.
- behindFullscreen |= task.mFullscreen;
+ if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ // The visibility of tasks and the activities they contain in freeform stack are
+ // determined individually unlike other stacks where the visibility or fullscreen
+ // status of an activity in a previous task affects other.
+ behindFullscreenActivity = stackInvisible;
+ }
}
if (mTranslucentActivityWaiting != null &&
@@ -1948,7 +1957,6 @@
? AppTransition.TRANSIT_ACTIVITY_CLOSE
: AppTransition.TRANSIT_TASK_CLOSE, false);
}
- mWindowManager.setAppWillBeHidden(prev.appToken);
mWindowManager.setAppVisibility(prev.appToken, false);
} else {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
@@ -1964,10 +1972,6 @@
: AppTransition.TRANSIT_TASK_OPEN, false);
}
}
- if (false) {
- mWindowManager.setAppWillBeHidden(prev.appToken);
- mWindowManager.setAppVisibility(prev.appToken, false);
- }
} else {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
if (mNoAnimActivities.contains(next)) {
@@ -2340,7 +2344,7 @@
// Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
// tell WindowManager that r is visible even though it is at the back of the stack.
mWindowManager.setAppVisibility(r.appToken, true);
- ensureActivitiesVisibleLocked(null, 0);
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
} else if (SHOW_APP_STARTING_PREVIEW && doShow) {
// Figure out if we are transitioning from another activity that is
// "has the same starting icon" as the next one. This allows the
@@ -3499,7 +3503,7 @@
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
"Removing activity from " + reason + ": token=" + r
- + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+ + ", app=" + (r.app != null ? r.app.processName : "(null)"));
EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
r.userId, System.identityHashCode(r),
r.task.taskId, r.shortComponentName, reason);
@@ -3976,7 +3980,8 @@
* for whatever reason. Ensures the HistoryRecord is updated with the
* correct configuration and all other bookkeeping is handled.
*/
- final boolean ensureActivityConfigurationLocked(ActivityRecord r, int globalChanges) {
+ final boolean ensureActivityConfigurationLocked(ActivityRecord r, int globalChanges,
+ boolean preserveWindow) {
if (mConfigWillChange) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check (will change): " + r);
@@ -4021,6 +4026,11 @@
return true;
}
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+ "Configuration changes for " + r + " ; taskChanges="
+ + Configuration.configurationDiffToString(taskChanges) + ", allChanges="
+ + Configuration.configurationDiffToString(changes));
+
// If the activity isn't currently running, just leave the new
// configuration and it will pick that up next time it starts.
if (r.app == null || r.app.thread == null) {
@@ -4061,12 +4071,14 @@
// "restart!".
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, true);
+ relaunchActivityLocked(r, r.configChangeFlags, true,
+ preserveWindow && isResizeOnlyChange(changes));
r.configChangeFlags = 0;
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching non-resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, false);
+ relaunchActivityLocked(r, r.configChangeFlags, false,
+ preserveWindow && isResizeOnlyChange(changes));
r.configChangeFlags = 0;
}
@@ -4159,7 +4171,17 @@
return taskChanges;
}
- private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) {
+ private static boolean isResizeOnlyChange(int change) {
+ return (change & ~(ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) == 0;
+ }
+
+ private void relaunchActivityLocked(
+ ActivityRecord r, int changes, boolean andResume, boolean preserveWindow) {
+ if (mService.mSuppressResizeConfigChanges && preserveWindow) {
+ return;
+ }
+
List<ResultInfo> results = null;
List<ReferrerIntent> newIntents = null;
if (andResume) {
@@ -4168,7 +4190,7 @@
}
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
"Relaunching: " + r + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
+ + " andResume=" + andResume + " preserveWindow=" + preserveWindow);
EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
: EventLogTags.AM_RELAUNCH_ACTIVITY, r.userId, System.identityHashCode(r),
r.task.taskId, r.shortComponentName);
@@ -4183,7 +4205,7 @@
r.forceNewConfig = false;
r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
!andResume, new Configuration(mService.mConfiguration),
- new Configuration(r.task.mOverrideConfig));
+ new Configuration(r.task.mOverrideConfig), preserveWindow);
// Note: don't need to call pauseIfSleepingLocked() here, because
// the caller will only pass in 'andResume' if this activity is
// currently resumed, which implies we aren't sleeping.
@@ -4199,8 +4221,6 @@
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
r.state = ActivityState.PAUSED;
}
-
- return true;
}
boolean willActivityBeVisibleLocked(IBinder token) {
@@ -4525,18 +4545,17 @@
if (mTaskHistory.isEmpty()) {
if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
- final boolean notHomeStack = !isHomeStack();
if (isOnHomeDisplay()) {
String myReason = reason + " leftTaskHistoryEmpty";
if (mFullscreen || !adjustFocusToNextVisibleStackLocked(null, myReason)) {
- mStackSupervisor.moveHomeStack(notHomeStack, myReason);
+ mStackSupervisor.moveHomeStackToFront(myReason);
}
}
if (mStacks != null) {
mStacks.remove(this);
mStacks.add(0, this);
}
- if (notHomeStack) {
+ if (!isHomeStack()) {
mActivityContainer.onTaskListEmptyLocked();
}
}
@@ -4553,6 +4572,8 @@
addTask(task, toTop, false);
if (mTaskPositioner != null) {
mTaskPositioner.updateDefaultBounds(task, mTaskHistory, info.initialLayout);
+ } else if (mBounds != null && task.mResizeable) {
+ task.updateOverrideConfiguration(mBounds);
}
return task;
}
@@ -4595,7 +4616,7 @@
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind,
- bounds, task.mOverrideConfig);
+ bounds, task.mOverrideConfig, !r.isHomeActivity());
r.taskConfigOverride = task.mOverrideConfig;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index dd9b6f1..6b5f205 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -24,6 +24,7 @@
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -87,6 +88,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -175,6 +177,9 @@
// should be created if it doesn't exist already.
private static final boolean CREATE_IF_NEEDED = true;
+ // Used to indicate that windows of activities should be preserved during the resize.
+ static final boolean PRESERVE_WINDOWS = true;
+
// Used to indicate if an object (e.g. task) should be moved/created
// at the top of its container (e.g. stack).
static final boolean ON_TOP = true;
@@ -189,6 +194,7 @@
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
+
static {
ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
Manifest.permission.CAMERA);
@@ -205,6 +211,13 @@
/** Action restriction: launching the activity is restricted by an app op. */
private static final int ACTIVITY_RESTRICTION_APPOP = 2;
+ // The height/width divide used when fitting a task within a bounds with method
+ // {@link #fitWithinBounds}.
+ // We always want the task to to be visible in the bounds without affecting its size when
+ // fitting. To make sure this is the case, we don't adjust the task left or top side pass
+ // the input bounds right or bottom side minus the width or height divided by this value.
+ private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
+
/** Status Bar Service **/
private IBinder mToken = new Binder();
private IStatusBarService mStatusBarService;
@@ -330,8 +343,9 @@
/** Used to keep resumeTopActivityLocked() from being entered recursively */
boolean inResumeTopActivity;
- // temp. rect used during resize calculation so we don't need to create a new object each time.
+ // temp. rects used during resize calculation so we don't need to create a new object each time.
private final Rect tempRect = new Rect();
+ private final Rect tempRect2 = new Rect();
private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>();
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
@@ -456,35 +470,22 @@
return stack == mFocusedStack;
}
- void moveHomeStack(boolean toFront, String reason) {
- moveHomeStack(toFront, reason, null);
- }
-
- void moveHomeStack(boolean toFront, String reason, ActivityStack lastFocusedStack) {
+ void setFocusStack(String reason, ActivityStack lastFocusedStack) {
ArrayList<ActivityStack> stacks = mHomeStack.mStacks;
final int topNdx = stacks.size() - 1;
if (topNdx <= 0) {
return;
}
- // The home stack should either be at the top or bottom of the stack list.
- if ((toFront && (stacks.get(topNdx) != mHomeStack))
- || (!toFront && (stacks.get(0) != mHomeStack))) {
- if (DEBUG_STACK) Slog.d(TAG_STACK, "moveHomeTask: topStack old="
- + ((lastFocusedStack != null) ? lastFocusedStack : stacks.get(topNdx))
- + " new=" + mFocusedStack);
- stacks.remove(mHomeStack);
- stacks.add(toFront ? topNdx : 0, mHomeStack);
- }
-
+ final ActivityStack topStack = stacks.get(topNdx);
+ mFocusedStack = topStack;
if (lastFocusedStack != null) {
mLastFocusedStack = lastFocusedStack;
}
- mFocusedStack = stacks.get(topNdx);
- EventLog.writeEvent(EventLogTags.AM_HOME_STACK_MOVED,
- mCurrentUser, toFront ? 1 : 0, stacks.get(topNdx).getStackId(),
- mFocusedStack == null ? -1 : mFocusedStack.getStackId(), reason);
+ EventLogTags.writeAmFocusedStack(
+ mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
+ mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
if (mService.mBooting || !mService.mBooted) {
final ActivityRecord r = topRunningActivityLocked();
@@ -494,6 +495,10 @@
}
}
+ void moveHomeStackToFront(String reason) {
+ mHomeStack.moveToFront(reason);
+ }
+
/** Returns true if the focus activity was adjusted to the home stack top activity. */
boolean moveHomeStackTaskToTop(int homeStackTaskType, String reason) {
if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
@@ -652,7 +657,7 @@
}
}
if (!didSomething) {
- ensureActivitiesVisibleLocked(null, 0);
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
return didSomething;
}
@@ -1345,8 +1350,7 @@
r.launchFailed = false;
if (stack.updateLRUListLocked(r)) {
- Slog.w(TAG, "Activity " + r
- + " being launched, but already in LRU list");
+ Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
}
if (andResume) {
@@ -1795,8 +1799,11 @@
return container.mStack;
}
- if (mFocusedStack != mHomeStack && (!newTask ||
- mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
+ // The fullscreen stack is the only stack that can contain any task regardless of if
+ // the task is resizeable or not. So, we let the task go in the fullscreen task if it
+ // is the focus stack.
+ if (mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+ && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Have a focused stack=" + mFocusedStack);
return mFocusedStack;
@@ -1815,7 +1822,7 @@
}
// If there is no suitable dynamic stack then we figure out which static stack to use.
- int stackId = task != null ? task.getLaunchStackId() :
+ final int stackId = task != null ? task.getLaunchStackId() :
bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
FULLSCREEN_WORKSPACE_STACK_ID;
stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
@@ -2621,7 +2628,7 @@
}
mLaunchingActivity.release();
}
- ensureActivitiesVisibleLocked(null, 0);
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
// Atomically retrieve all of the other things to do.
@@ -2851,7 +2858,8 @@
if (opts.hasBounds()) {
Rect bounds = opts.getBounds();
task.updateOverrideConfiguration(bounds);
- mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, false);
+ mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig,
+ false /*relayout*/, false /*forced*/);
stackId = task.getLaunchStackId();
}
}
@@ -2954,29 +2962,39 @@
}
}
- void resizeStackLocked(int stackId, Rect bounds) {
+ void resizeStackLocked(int stackId, Rect bounds, boolean preserveWindows) {
final ActivityStack stack = getStack(stackId);
if (stack == null) {
Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
return;
}
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
+
ActivityRecord r = stack.topRunningActivityLocked(null);
- final boolean resizeTasks = r != null && r.task.mResizeable;
mTmpBounds.clear();
mTmpConfigs.clear();
- if (resizeTasks) {
- ArrayList<TaskRecord> tasks = stack.getAllTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- TaskRecord task = tasks.get(i);
- task.updateOverrideConfiguration(bounds);
- mTmpConfigs.put(task.taskId, task.mOverrideConfig);
- mTmpBounds.put(task.taskId, task.mBounds);
+ ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ TaskRecord task = tasks.get(i);
+ if (task.mResizeable) {
+ if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ // For freeform stack we don't adjust the size of the tasks to match that
+ // of the stack, but we do try to make sure the tasks are still contained
+ // with the bounds of the stack.
+ tempRect2.set(task.mBounds);
+ fitWithinBounds(tempRect2, bounds);
+ task.updateOverrideConfiguration(tempRect2);
+ } else {
+ task.updateOverrideConfiguration(bounds);
+ }
}
+
+ mTmpConfigs.put(task.taskId, task.mOverrideConfig);
+ mTmpBounds.put(task.taskId, task.mBounds);
}
- stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, resizeTasks, mTmpConfigs,
- mTmpBounds);
+ stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds);
if (stack.mStackId == DOCKED_STACK_ID) {
// Dock stack funness...Yay!
if (stack.mFullscreen) {
@@ -2985,11 +3003,10 @@
// docked stack tasks to the fullscreen stack.
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
if (i != DOCKED_STACK_ID) {
- resizeStackLocked(i, null);
+ resizeStackLocked(i, null, preserveWindows);
}
}
- final ArrayList<TaskRecord> tasks = stack.getAllTasks();
final int count = tasks.size();
for (int i = 0; i < count; i++) {
moveTaskToStackLocked(tasks.get(i).taskId,
@@ -3017,33 +3034,40 @@
tempRect.right -= leftChange;
tempRect.top -= bottomChange;
tempRect.bottom -= topChange;
- resizeStackLocked(i, tempRect);
+ resizeStackLocked(i, tempRect, PRESERVE_WINDOWS);
}
}
}
-
}
+ // Since we are resizing the stack, all other operations should strive to preserve
+ // windows.
+ preserveWindows = true;
}
stack.setBounds(bounds);
if (r != null) {
- final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
+ final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
- ensureActivitiesVisibleLocked(r, 0);
+ ensureActivitiesVisibleLocked(r, 0, preserveWindows);
if (!updated) {
resumeTopActivitiesLocked(stack, null, null);
}
}
+
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
- void resizeTaskLocked(TaskRecord task, Rect bounds) {
+ void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode) {
if (!task.mResizeable) {
Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
return;
}
- if (task.mBounds != null && task.mBounds.equals(bounds)) {
+ // If this is a forced resize, let it go through even if the bounds is not changing,
+ // as we might need a relayout due to surface size change (to/from fullscreen).
+ final boolean forced = (resizeMode == RESIZE_MODE_FORCED);
+ if (task.mBounds != null && task.mBounds.equals(bounds) && !forced) {
// Nothing to do here...
return;
}
@@ -3060,6 +3084,8 @@
return;
}
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId);
+
// The stack of a task is determined by its size (fullscreen vs non-fullscreen).
// Place the task in the right stack if it isn't there already based on the requested
// bounds.
@@ -3070,7 +3096,8 @@
&& stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) {
stackId = FREEFORM_WORKSPACE_STACK_ID;
}
- if (stackId != task.stack.mStackId) {
+ final boolean changedStacks = stackId != task.stack.mStackId;
+ if (changedStacks) {
moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
}
@@ -3083,24 +3110,20 @@
ActivityRecord r = task.topRunningActivityLocked(null);
if (r != null) {
final ActivityStack stack = task.stack;
- kept = stack.ensureActivityConfigurationLocked(r, 0);
+ final boolean preserveWindow = !changedStacks &&
+ (resizeMode == RESIZE_MODE_USER
+ || resizeMode == RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
+ kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
// All other activities must be made visible with their correct configuration.
- ensureActivitiesVisibleLocked(r, 0);
+ ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
if (!kept) {
resumeTopActivitiesLocked(stack, null, null);
- // We are about to relaunch the activity because its configuration changed due
- // to size change. The activity will first remove the old window and then add a
- // new one. This call will tell window manager about this, so it can preserve
- // the old window until the new one is drawn. This prevents having a gap between
- // the removal and addition, in which no window is visible. If we also changed
- // the stack to the fullscreen stack, i.e. maximized the window, we will animate
- // the transition.
- mWindowManager.setReplacingWindow(r.appToken,
- stackId == FULLSCREEN_WORKSPACE_STACK_ID /* animate */);
}
}
}
- mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, kept);
+ mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, kept, forced);
+
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
@@ -3187,7 +3210,13 @@
final boolean wasFocused = isFrontStack(task.stack) && (topRunningActivityLocked() == r);
final boolean wasResumed = wasFocused && (task.stack.mResumedActivity == r);
+ final boolean resizeable = task.mResizeable;
+ // Temporarily disable resizeablility of task we are moving. We don't want it to be resized
+ // if a docked stack is created below which will lead to the stack we are moving from and
+ // its resizeable tasks being resized.
+ task.mResizeable = false;
final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
+ task.mResizeable = resizeable;
mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop);
if (task.stack != null) {
task.stack.removeTask(task, reason, MOVING);
@@ -3217,22 +3246,32 @@
return;
}
final String reason = "moveTaskToStack";
+ if (stackId == DOCKED_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ // We are about to relaunch the activity because its configuration changed due to
+ // being maximized, i.e. size change. The activity will first remove the old window
+ // and then add a new one. This call will tell window manager about this, so it can
+ // preserve the old window until the new one is drawn. This prevents having a gap
+ // between the removal and addition, in which no window is visible. We also want the
+ // entrace of the new window to be properly animated.
+ ActivityRecord r = task.getTopActivity();
+ mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
+ }
final ActivityStack stack =
moveTaskToStackUncheckedLocked(task, stackId, toTop, forceFocus, reason);
// Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
- resizeTaskLocked(task, stack.mBounds);
+ resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM);
} else if (stackId == FREEFORM_WORKSPACE_STACK_ID
&& task.mBounds == null && task.mLastNonFullscreenBounds != null) {
- resizeTaskLocked(task, task.mLastNonFullscreenBounds);
+ resizeTaskLocked(task, task.mLastNonFullscreenBounds, RESIZE_MODE_SYSTEM);
} else if (stackId == DOCKED_STACK_ID) {
- resizeTaskLocked(task, stack.mBounds);
+ resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM);
}
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
- ensureActivitiesVisibleLocked(null, 0);
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeTopActivitiesLocked();
}
@@ -3252,7 +3291,7 @@
stack.positionTask(task, position, stackChanged);
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
- stack.ensureActivitiesVisibleLocked(null, 0);
+ stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeTopActivitiesLocked();
}
@@ -3427,7 +3466,7 @@
mService.updateUsageStats(r, true);
}
if (allResumedActivitiesComplete()) {
- ensureActivitiesVisibleLocked(null, 0);
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mWindowManager.executeAppTransition();
return true;
}
@@ -3513,14 +3552,15 @@
mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
}
- void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
+ void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+ boolean preserveWindows) {
// First the front stacks. In case any are not fullscreen and are in front of home.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
final int topStackNdx = stacks.size() - 1;
for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- stack.ensureActivitiesVisibleLocked(starting, configChanges);
+ stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows);
}
}
}
@@ -3627,11 +3667,7 @@
}
final boolean homeInFront = stack.isHomeStack();
if (stack.isOnHomeDisplay()) {
- moveHomeStack(homeInFront, "switchUserOnHomeDisplay");
- TaskRecord task = stack.topTask();
- if (task != null) {
- mWindowManager.moveTaskToTop(task.taskId);
- }
+ stack.moveToFront("switchUserOnHomeDisplay");
} else {
// Stack was moved to another display while user was swapped out.
resumeHomeStackTask(HOME_ACTIVITY_TYPE, null, "switchUserOnOtherDisplay");
@@ -4591,16 +4627,6 @@
return mActivityDisplay != null;
}
- void getBounds(Point outBounds) {
- synchronized (mService) {
- if (mActivityDisplay != null) {
- mActivityDisplay.getBounds(outBounds);
- } else {
- outBounds.set(0, 0);
- }
- }
- }
-
// TODO: Make sure every change to ActivityRecord.visible results in a call to this.
void setVisible(boolean visible) {
if (mVisible != visible) {
@@ -4772,12 +4798,6 @@
mStacks.remove(stack);
}
- void getBounds(Point bounds) {
- mDisplay.getDisplayInfo(mDisplayInfo);
- bounds.x = mDisplayInfo.appWidth;
- bounds.y = mDisplayInfo.appHeight;
- }
-
void setVisibleBehindActivity(ActivityRecord r) {
mVisibleBehindActivity = r;
}
@@ -4839,4 +4859,44 @@
return onLeanbackOnly;
}
+
+ /**
+ * Adjust bounds to stay within stack bounds.
+ *
+ * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
+ * that keep them unchanged, but be contained within the stack bounds.
+ *
+ * @param bounds Bounds to be adjusted.
+ * @param stackBounds Bounds within which the other bounds should remain.
+ */
+ private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
+ if (stackBounds == null || stackBounds.contains(bounds)) {
+ return;
+ }
+
+ if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
+ final int maxRight = stackBounds.right
+ - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
+ int horizontalDiff = stackBounds.left - bounds.left;
+ if ((horizontalDiff < 0 && bounds.left >= maxRight)
+ || (bounds.left + horizontalDiff >= maxRight)) {
+ horizontalDiff = maxRight - bounds.left;
+ }
+ bounds.left += horizontalDiff;
+ bounds.right += horizontalDiff;
+ }
+
+ if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
+ final int maxBottom = stackBounds.bottom
+ - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
+ int verticalDiff = stackBounds.top - bounds.top;
+ if ((verticalDiff < 0 && bounds.top >= maxBottom)
+ || (bounds.top + verticalDiff >= maxBottom)) {
+ verticalDiff = maxBottom - bounds.top;
+ }
+ bounds.top += verticalDiff;
+ bounds.bottom += verticalDiff;
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 335288d..62768c3 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -171,12 +171,11 @@
public void publish(Context context) {
mContext = context;
- ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
- mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps());
mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout)
* 1000L);
mStats.setPowerProfile(new PowerProfile(context));
+ ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
}
/**
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 814e8b4..c36fd06 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import java.io.File;
import java.io.FileInputStream;
@@ -344,10 +345,10 @@
}
if (starting != null) {
- stack.ensureActivityConfigurationLocked(starting, 0);
+ stack.ensureActivityConfigurationLocked(starting, 0, false);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
- stack.ensureActivitiesVisibleLocked(starting, 0);
+ stack.ensureActivitiesVisibleLocked(starting, 0, !PRESERVE_WINDOWS);
}
}
}
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9a645df..78b5f33 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -93,8 +93,8 @@
# Activity focused
30043 am_focused_activity (User|1|5),(Component Name|3)
-# Home Stack brought to front or rear
-30044 am_home_stack_moved (User|1|5),(To Front|1|5),(Top Stack Id|1|5),(Focused Stack Id|1|5),(Reason|3)
+# Stack focus
+30044 am_focused_stack (User|1|5),(Focused Stack Id|1|5),(Last Focused Stack Id|1|5),(Reason|3)
# Running pre boot receiver
30045 am_pre_boot (User|1|5),(Package|3)
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index ed8b1dd..878c0e7a 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -211,7 +211,7 @@
boolean doit, boolean evenPersistent, int userId,
ArrayList<ContentProviderRecord> result) {
boolean didSomething = false;
- if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_OWNER) {
+ if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_SYSTEM) {
didSomething = collectPackageProvidersLocked(packageName, filterByClasses,
doit, evenPersistent, mSingletonByClass, result);
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 5694e704..43f5baa 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -108,13 +108,6 @@
static final int INVALID_TASK_ID = -1;
- // The height/width divide used when fitting a task within a bounds with method
- // {@link #fitWithinBounds}.
- // We always want the task to to be visible in the bounds without affecting its size when
- // fitting. To make sure this is the case, we don't adjust the task left or top side pass
- // the input bounds right or bottom side minus the width or height divided by this value.
- private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
-
final int taskId; // Unique identifier for this task.
String affinity; // The affinity name for this task, or null; may change identity.
String rootAffinity; // Initial base affinity, or null; does not change from initial root.
@@ -1186,12 +1179,6 @@
* @return Update configuration or null if there is no change.
*/
Configuration updateOverrideConfiguration(Rect bounds) {
- if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
- // For freeform stack we don't adjust the size of the tasks to match that of the
- // stack, but we do try to make sure the tasks are still contained with the
- // bounds of the stack.
- fitWithinBounds(bounds, stack.mBounds);
- }
if (Objects.equals(mBounds, bounds)) {
return null;
}
@@ -1248,52 +1235,13 @@
if (stack == null
|| stack.mStackId == HOME_STACK_ID
|| stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
- return null;
+ return (mResizeable && stack != null) ? stack.mBounds : null;
} else if (stack.mStackId == DOCKED_STACK_ID) {
return stack.mBounds;
}
return mLastNonFullscreenBounds;
}
- /**
- * Adjust bounds to stay within stack bounds.
- *
- * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
- * that keep them unchanged, but be contained within the stack bounds.
- *
- * @param bounds Bounds to be adjusted.
- * @param stackBounds Bounds within which the other bounds should remain.
- */
- private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
- if (stackBounds == null || stackBounds.contains(bounds)) {
- return;
- }
-
- if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
- final int maxRight = stackBounds.right
- - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
- int horizontalDiff = stackBounds.left - bounds.left;
- if ((horizontalDiff < 0 && bounds.left >= maxRight)
- || (bounds.left + horizontalDiff >= maxRight)) {
- horizontalDiff = maxRight - bounds.left;
- }
- bounds.left += horizontalDiff;
- bounds.right += horizontalDiff;
- }
-
- if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
- final int maxBottom = stackBounds.bottom
- - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
- int verticalDiff = stackBounds.top - bounds.top;
- if ((verticalDiff < 0 && bounds.top >= maxBottom)
- || (bounds.top + verticalDiff >= maxBottom)) {
- verticalDiff = maxBottom - bounds.top;
- }
- bounds.top += verticalDiff;
- bounds.bottom += verticalDiff;
- }
- }
-
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("userId="); pw.print(userId);
pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1475f2f..e49a7e4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1560,7 +1560,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- return UserHandle.USER_OWNER;
+ return UserHandle.USER_SYSTEM;
}
// UI update and Broadcast Intent
@@ -2524,11 +2524,14 @@
}
/** @see AudioManager#setBluetoothScoOn(boolean) */
- public void setBluetoothScoOn(boolean on){
+ public void setBluetoothScoOn(boolean on) {
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
return;
}
+ setBluetoothScoOnInt(on);
+ }
+ public void setBluetoothScoOnInt(boolean on) {
if (on) {
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
@@ -2889,6 +2892,8 @@
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
+ AudioSystem.setParameters("A2dpSuspended=false");
+ setBluetoothScoOnInt(false);
}
private void broadcastScoConnectionState(int state) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index aca6991..5fd39c0 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -18,6 +18,7 @@
import static android.system.OsConstants.*;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkUtils;
@@ -27,6 +28,7 @@
import android.system.Os;
import android.system.StructTimeval;
import android.text.TextUtils;
+import android.util.Pair;
import com.android.internal.util.IndentingPrintWriter;
@@ -149,6 +151,8 @@
}
private final Map<InetAddress, Measurement> mIcmpChecks = new HashMap<>();
+ private final Map<Pair<InetAddress, InetAddress>, Measurement> mExplicitSourceIcmpChecks =
+ new HashMap<>();
private final Map<InetAddress, Measurement> mDnsUdpChecks = new HashMap<>();
private final String mDescription;
@@ -178,7 +182,11 @@
for (RouteInfo route : mLinkProperties.getRoutes()) {
if (route.hasGateway()) {
- prepareIcmpMeasurement(route.getGateway());
+ InetAddress gateway = route.getGateway();
+ prepareIcmpMeasurement(gateway);
+ if (route.isIPv6Default()) {
+ prepareExplicitSourceIcmpMeasurements(gateway);
+ }
}
}
for (InetAddress nameserver : mLinkProperties.getDnsServers()) {
@@ -213,6 +221,20 @@
}
}
+ private void prepareExplicitSourceIcmpMeasurements(InetAddress target) {
+ for (LinkAddress l : mLinkProperties.getLinkAddresses()) {
+ InetAddress source = l.getAddress();
+ if (source instanceof Inet6Address && l.isGlobalPreferred()) {
+ Pair<InetAddress, InetAddress> srcTarget = new Pair<>(source, target);
+ if (!mExplicitSourceIcmpChecks.containsKey(srcTarget)) {
+ Measurement measurement = new Measurement();
+ measurement.thread = new Thread(new IcmpCheck(source, target, measurement));
+ mExplicitSourceIcmpChecks.put(srcTarget, measurement);
+ }
+ }
+ }
+ }
+
private void prepareDnsMeasurement(InetAddress target) {
if (!mDnsUdpChecks.containsKey(target)) {
Measurement measurement = new Measurement();
@@ -222,13 +244,16 @@
}
private int totalMeasurementCount() {
- return mIcmpChecks.size() + mDnsUdpChecks.size();
+ return mIcmpChecks.size() + mExplicitSourceIcmpChecks.size() + mDnsUdpChecks.size();
}
private void startMeasurements() {
for (Measurement measurement : mIcmpChecks.values()) {
measurement.thread.start();
}
+ for (Measurement measurement : mExplicitSourceIcmpChecks.values()) {
+ measurement.thread.start();
+ }
for (Measurement measurement : mDnsUdpChecks.values()) {
measurement.thread.start();
}
@@ -261,6 +286,10 @@
pw.println(entry.getValue().toString());
}
}
+ for (Map.Entry<Pair<InetAddress, InetAddress>, Measurement> entry :
+ mExplicitSourceIcmpChecks.entrySet()) {
+ pw.println(entry.getValue().toString());
+ }
for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
if (entry.getKey() instanceof Inet4Address) {
pw.println(entry.getValue().toString());
@@ -276,13 +305,15 @@
private class SimpleSocketCheck implements Closeable {
+ protected final InetAddress mSource; // Usually null.
protected final InetAddress mTarget;
protected final int mAddressFamily;
protected final Measurement mMeasurement;
protected FileDescriptor mFileDescriptor;
protected SocketAddress mSocketAddress;
- protected SimpleSocketCheck(InetAddress target, Measurement measurement) {
+ protected SimpleSocketCheck(
+ InetAddress source, InetAddress target, Measurement measurement) {
mMeasurement = measurement;
if (target instanceof Inet6Address) {
@@ -301,6 +332,14 @@
mTarget = target;
mAddressFamily = AF_INET;
}
+
+ // We don't need to check the scope ID here because we currently only do explicit-source
+ // measurements from global IPv6 addresses.
+ mSource = source;
+ }
+
+ protected SimpleSocketCheck(InetAddress target, Measurement measurement) {
+ this(null, target, measurement);
}
protected void setupSocket(
@@ -314,6 +353,9 @@
SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(readTimeout));
// TODO: Use IP_RECVERR/IPV6_RECVERR, pending OsContants availability.
mNetwork.bindSocket(mFileDescriptor);
+ if (mSource != null) {
+ Os.bind(mFileDescriptor, mSource, 0);
+ }
Os.connect(mFileDescriptor, mTarget, dstPort);
mSocketAddress = Os.getsockname(mFileDescriptor);
}
@@ -343,8 +385,8 @@
private final int mProtocol;
private final int mIcmpType;
- public IcmpCheck(InetAddress target, Measurement measurement) {
- super(target, measurement);
+ public IcmpCheck(InetAddress source, InetAddress target, Measurement measurement) {
+ super(source, target, measurement);
if (mAddressFamily == AF_INET6) {
mProtocol = IPPROTO_ICMPV6;
@@ -359,6 +401,10 @@
mMeasurement.description += " dst{" + mTarget.getHostAddress() + "}";
}
+ public IcmpCheck(InetAddress target, Measurement measurement) {
+ this(null, target, measurement);
+ }
+
@Override
public void run() {
// Check if this measurement has already failed during setup.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 3df3cd0..2bea278 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -20,9 +20,6 @@
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
-import static android.os.UserHandle.PER_USER_RANGE;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
import android.Manifest;
import android.app.AppGlobals;
@@ -34,13 +31,11 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -126,7 +121,6 @@
/* list of users using this VPN. */
@GuardedBy("this")
private List<UidRange> mVpnUsers = null;
- private BroadcastReceiver mUserIntentReceiver = null;
// Handle of user initiating VPN.
private final int mUserHandle;
@@ -146,31 +140,6 @@
} catch (RemoteException e) {
Log.wtf(TAG, "Problem registering observer", e);
}
- // TODO: http://b/22950929
- if (userHandle == UserHandle.USER_SYSTEM) {
- // Owner's VPN also needs to handle restricted users
- mUserIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL);
- if (userHandle == UserHandle.USER_NULL) return;
-
- if (Intent.ACTION_USER_ADDED.equals(action)) {
- onUserAdded(userHandle);
- } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- onUserRemoved(userHandle);
- }
- }
- };
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_ADDED);
- intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiverAsUser(
- mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
- }
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
// TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
@@ -439,8 +408,8 @@
}
addVpnUserLocked(mUserHandle);
- // If we are owner assign all Restricted Users to this VPN
- if (mUserHandle == UserHandle.USER_OWNER) {
+ // If the user can have restricted profiles, assign all its restricted profiles to this VPN
+ if (canHaveRestrictedProfile(mUserHandle)) {
token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
@@ -449,7 +418,7 @@
Binder.restoreCallingIdentity(token);
}
for (UserInfo user : users) {
- if (user.isRestricted()) {
+ if (user.isRestricted() && (user.restrictedProfileParentId == mUserHandle)) {
addVpnUserLocked(user.id);
}
}
@@ -457,6 +426,15 @@
mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
}
+ private boolean canHaveRestrictedProfile(int userId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return UserManager.get(mContext).canHaveRestrictedProfile(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
networkInfo.setIsAvailable(false);
networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
@@ -681,12 +659,11 @@
mStatusIntent = null;
}
- private void onUserAdded(int userHandle) {
- // If the user is restricted tie them to the owner's VPN
- synchronized(Vpn.this) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(userHandle);
- if (user.isRestricted()) {
+ public void onUserAdded(int userHandle) {
+ // If the user is restricted tie them to the parent user's VPN
+ UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ synchronized(Vpn.this) {
try {
addVpnUserLocked(userHandle);
if (mNetworkAgent != null) {
@@ -700,12 +677,11 @@
}
}
- private void onUserRemoved(int userHandle) {
+ public void onUserRemoved(int userHandle) {
// clean up if restricted
- synchronized(Vpn.this) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(userHandle);
- if (user.isRestricted()) {
+ UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ synchronized(Vpn.this) {
try {
removeVpnUserLocked(userHandle);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 292aff9..e6dc895 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -340,7 +340,8 @@
for (UserInfo user : mUserManager.getUsers(true)) {
// Skip any partially created/removed users
if (user.partial) continue;
- Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id);
+ Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(
+ user.id, mContext.getOpPackageName());
mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
}
}
@@ -1232,7 +1233,8 @@
}
// Schedule sync for any accounts under started user
- final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId);
+ final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId,
+ mContext.getOpPackageName());
for (Account account : accounts) {
scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
0 /* no delay */, 0 /* No flex */,
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 6ba25a5..701b9f1 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -49,6 +49,13 @@
*/
private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1); // 0 = no mode.
+ /**
+ * Used to generate globally unique color transform ids.
+ *
+ * Valid IDs start at 1 with 0 as the sentinel value for the default mode.
+ */
+ private static final AtomicInteger NEXT_COLOR_TRANSFORM_ID = new AtomicInteger(1);
+
// Called with SyncRoot lock held.
public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener, String name) {
@@ -134,6 +141,11 @@
NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate);
}
+ public static Display.ColorTransform createColorTransform(int colorTransform) {
+ return new Display.ColorTransform(
+ NEXT_COLOR_TRANSFORM_ID.getAndIncrement(), colorTransform);
+ }
+
public interface Listener {
public void onDisplayDeviceEvent(DisplayDevice device, int event);
public void onTraversalRequested();
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 93bda46..7af0bdb 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -135,7 +135,7 @@
/**
* Sets the mode, if supported.
*/
- public void requestModeInTransactionLocked(int id) {
+ public void requestColorTransformAndModeInTransactionLocked(int colorTransformId, int modeId) {
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 97ada15..55ba302 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -155,6 +155,15 @@
*/
public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
+ /** The active color transform of the display */
+ public int colorTransformId;
+
+ /** The default color transform of the display */
+ public int defaultColorTransformId;
+
+ /** The supported color transforms of the display */
+ public Display.ColorTransform[] supportedColorTransforms = Display.ColorTransform.EMPTY_ARRAY;
+
/**
* The nominal apparent density of the display in DPI used for layout calculations.
* This density is sensitive to the viewing distance. A big TV and a tablet may have
@@ -276,6 +285,9 @@
|| modeId != other.modeId
|| defaultModeId != other.defaultModeId
|| !Arrays.equals(supportedModes, other.supportedModes)
+ || colorTransformId != other.colorTransformId
+ || defaultColorTransformId != other.defaultColorTransformId
+ || !Arrays.equals(supportedColorTransforms, other.supportedColorTransforms)
|| densityDpi != other.densityDpi
|| xDpi != other.xDpi
|| yDpi != other.yDpi
@@ -306,6 +318,9 @@
modeId = other.modeId;
defaultModeId = other.defaultModeId;
supportedModes = other.supportedModes;
+ colorTransformId = other.colorTransformId;
+ defaultColorTransformId = other.defaultColorTransformId;
+ supportedColorTransforms = other.supportedColorTransforms;
densityDpi = other.densityDpi;
xDpi = other.xDpi;
yDpi = other.yDpi;
@@ -331,6 +346,9 @@
sb.append(", modeId ").append(modeId);
sb.append(", defaultModeId ").append(defaultModeId);
sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
+ sb.append(", colorTransformId ").append(colorTransformId);
+ sb.append(", defaultColorTransformId ").append(defaultColorTransformId);
+ sb.append(", supportedColorTransforms ").append(Arrays.toString(supportedColorTransforms));
sb.append(", density ").append(densityDpi);
sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
sb.append(", appVsyncOff ").append(appVsyncOffsetNanos);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b2ab797..6a6570b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -540,6 +540,17 @@
}
}
+ private void requestColorTransformInternal(int displayId, int colorTransformId) {
+ synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null &&
+ display.getRequestedColorTransformIdLocked() != colorTransformId) {
+ display.setRequestedColorTransformIdLocked(colorTransformId);
+ scheduleTraversalLocked(false);
+ }
+ }
+ }
+
private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
IMediaProjection projection, int callingUid, String packageName,
String name, int width, int height, int densityDpi, Surface surface, int flags) {
@@ -1340,6 +1351,19 @@
}
@Override // Binder call
+ public void requestColorTransform(int displayId, int colorTransformId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM,
+ "Permission required to change the display color transform");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ requestColorTransformInternal(displayId, colorTransformId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public int createVirtualDisplay(IVirtualDisplayCallback callback,
IMediaProjection projection, String packageName, String name,
int width, int height, int densityDpi, Surface surface, int flags) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index f53ccc9..2eabd32 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -341,7 +341,8 @@
private int mPendingBacklight = INITIAL_BACKLIGHT;
private int mActualState = INITIAL_SCREEN_STATE;
private int mActualBacklight = INITIAL_BACKLIGHT;
- private boolean mChangeInProgress;
+ private boolean mStateChangeInProgress;
+ private boolean mBacklightChangeInProgress;
public PhotonicModulator() {
super("PhotonicModulator");
@@ -349,7 +350,9 @@
public boolean setState(int state, int backlight) {
synchronized (mLock) {
- if (state != mPendingState || backlight != mPendingBacklight) {
+ boolean stateChanged = state != mPendingState;
+ boolean backlightChanged = backlight != mPendingBacklight;
+ if (stateChanged || backlightChanged) {
if (DEBUG) {
Slog.d(TAG, "Requesting new screen state: state="
+ Display.stateToString(state) + ", backlight=" + backlight);
@@ -358,12 +361,15 @@
mPendingState = state;
mPendingBacklight = backlight;
- if (!mChangeInProgress) {
- mChangeInProgress = true;
+ boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
+ mStateChangeInProgress = stateChanged;
+ mBacklightChangeInProgress = backlightChanged;
+
+ if (!changeInProgress) {
mLock.notifyAll();
}
}
- return !mChangeInProgress;
+ return !mStateChangeInProgress;
}
}
@@ -375,7 +381,8 @@
pw.println(" mPendingBacklight=" + mPendingBacklight);
pw.println(" mActualState=" + Display.stateToString(mActualState));
pw.println(" mActualBacklight=" + mActualBacklight);
- pw.println(" mChangeInProgress=" + mChangeInProgress);
+ pw.println(" mStateChangeInProgress=" + mStateChangeInProgress);
+ pw.println(" mBacklightChangeInProgress=" + mBacklightChangeInProgress);
}
}
@@ -392,10 +399,15 @@
stateChanged = (state != mActualState);
backlight = mPendingBacklight;
backlightChanged = (backlight != mActualBacklight);
- if (!stateChanged && !backlightChanged) {
- // All changed applied, notify outer class and wait for more.
- mChangeInProgress = false;
+ if (!stateChanged) {
+ // State changed applied, notify outer class.
postScreenUpdateThreadSafe();
+ mStateChangeInProgress = false;
+ }
+ if (!backlightChanged) {
+ mBacklightChangeInProgress = false;
+ }
+ if (!stateChanged && !backlightChanged) {
try {
mLock.wait();
} catch (InterruptedException ex) { }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 517a825..088d96e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -31,6 +31,7 @@
import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.Surface;
@@ -38,6 +39,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* A display adapter for the local displays managed by Surface Flinger.
@@ -143,14 +145,22 @@
private final int mBuiltInDisplayId;
private final Light mBacklight;
private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
+ private final SparseArray<Display.ColorTransform> mSupportedColorTransforms =
+ new SparseArray<>();
private DisplayDeviceInfo mInfo;
private boolean mHavePendingChanges;
private int mState = Display.STATE_UNKNOWN;
private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ private int mActivePhysIndex;
private int mDefaultModeId;
private int mActiveModeId;
private boolean mActiveModeInvalid;
+ private int mDefaultColorTransformId;
+ private int mActiveColorTransformId;
+ private boolean mActiveColorTransformInvalid;
+
+ private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[];
public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
@@ -167,22 +177,73 @@
public boolean updatePhysicalDisplayInfoLocked(
SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
- // Build an updated list of all existing modes.
- boolean modesAdded = false;
- DisplayModeRecord activeRecord = null;
- ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
+ mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length);
+ mActivePhysIndex = activeDisplayInfo;
+ ArrayList<Display.ColorTransform> colorTransforms = new ArrayList<>();
+
+ // Build an updated list of all existing color transforms.
+ boolean colorTransformsAdded = false;
+ Display.ColorTransform activeColorTransform = null;
for (int i = 0; i < physicalDisplayInfos.length; i++) {
SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
+ // First check to see if we've already added this color transform
+ boolean existingMode = false;
+ for (int j = 0; j < colorTransforms.size(); j++) {
+ if (colorTransforms.get(j).getColorTransform() == info.colorTransform) {
+ existingMode = true;
+ break;
+ }
+ }
+ if (existingMode) {
+ continue;
+ }
+ Display.ColorTransform colorTransform = findColorTransform(info);
+ if (colorTransform == null) {
+ colorTransform = createColorTransform(info.colorTransform);
+ colorTransformsAdded = true;
+ }
+ colorTransforms.add(colorTransform);
+ if (i == activeDisplayInfo) {
+ activeColorTransform = colorTransform;
+ }
+ }
+
+ // Build an updated list of all existing modes.
+ ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
+ boolean modesAdded = false;
+ for (int i = 0; i < physicalDisplayInfos.length; i++) {
+ SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
+ // First, check to see if we've already added a matching mode. Since not all
+ // configuration options are exposed via Display.Mode, it's possible that we have
+ // multiple PhysicalDisplayInfos that would generate the same Display.Mode.
+ boolean existingMode = false;
+ for (int j = 0; j < records.size(); j++) {
+ if (records.get(j).hasMatchingMode(info)) {
+ existingMode = true;
+ break;
+ }
+ }
+ if (existingMode) {
+ continue;
+ }
+ // If we haven't already added a mode for this configuration to the new set of
+ // supported modes then check to see if we have one in the prior set of supported
+ // modes to reuse.
DisplayModeRecord record = findDisplayModeRecord(info);
- if (record != null) {
- record.mPhysIndex = i;
- } else {
- record = new DisplayModeRecord(info, i);
+ if (record == null) {
+ record = new DisplayModeRecord(info);
modesAdded = true;
}
records.add(record);
- if (i == activeDisplayInfo) {
+ }
+
+ // Get the currently active mode
+ DisplayModeRecord activeRecord = null;
+ for (int i = 0; i < records.size(); i++) {
+ DisplayModeRecord record = records.get(i);
+ if (record.hasMatchingMode(physicalDisplayInfos[activeDisplayInfo])){
activeRecord = record;
+ break;
}
}
// Check whether surface flinger spontaneously changed modes out from under us. Schedule
@@ -192,25 +253,48 @@
mActiveModeInvalid = true;
sendTraversalRequestLocked();
}
- // If no modes were added and we have the same number of modes as before, then nothing
- // actually changed except possibly the physical index (which we only care about when
- // setting the mode) so we're done.
- if (records.size() == mSupportedModes.size() && !modesAdded) {
+ // Check whether surface flinger spontaneously changed color transforms out from under
+ // us.
+ if (mActiveColorTransformId != 0
+ && mActiveColorTransformId != activeColorTransform.getId()) {
+ mActiveColorTransformInvalid = true;
+ sendTraversalRequestLocked();
+ }
+
+ boolean colorTransformsChanged =
+ colorTransforms.size() != mSupportedColorTransforms.size()
+ || colorTransformsAdded;
+ boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
+ // If neither the records nor the supported color transforms have changed then we're
+ // done here.
+ if (!recordsChanged && !colorTransformsChanged) {
return false;
}
// Update the index of modes.
mHavePendingChanges = true;
+
mSupportedModes.clear();
for (DisplayModeRecord record : records) {
mSupportedModes.put(record.mMode.getModeId(), record);
}
- // Update the default mode if needed.
- if (mSupportedModes.indexOfKey(mDefaultModeId) < 0) {
+ mSupportedColorTransforms.clear();
+ for (Display.ColorTransform colorTransform : colorTransforms) {
+ mSupportedColorTransforms.put(colorTransform.getId(), colorTransform);
+ }
+
+ // Update the default mode and color transform if needed. This needs to be done in
+ // tandem so we always have a default state to fall back to.
+ if (findDisplayInfoIndexLocked(mDefaultColorTransformId, mDefaultModeId) < 0) {
if (mDefaultModeId != 0) {
- Slog.w(TAG, "Default display mode no longer available, using currently active"
- + " mode as default.");
+ Slog.w(TAG, "Default display mode no longer available, using currently"
+ + " active mode as default.");
}
mDefaultModeId = activeRecord.mMode.getModeId();
+ if (mDefaultColorTransformId != 0) {
+ Slog.w(TAG, "Default color transform no longer available, using currently"
+ + " active color transform as default");
+ }
+ mDefaultColorTransformId = activeColorTransform.getId();
}
// Determine whether the active mode is still there.
if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
@@ -221,6 +305,16 @@
mActiveModeId = mDefaultModeId;
mActiveModeInvalid = true;
}
+
+ // Determine whether the active color transform is still there.
+ if (mSupportedColorTransforms.indexOfKey(mActiveColorTransformId) < 0) {
+ if (mActiveColorTransformId != 0) {
+ Slog.w(TAG, "Active color transform no longer available, reverting"
+ + " to default transform.");
+ }
+ mActiveColorTransformId = mDefaultColorTransformId;
+ mActiveColorTransformInvalid = true;
+ }
// Schedule traversals so that we apply pending changes.
sendTraversalRequestLocked();
return true;
@@ -229,13 +323,23 @@
private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) {
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
- if (record.mPhys.equals(info)) {
+ if (record.hasMatchingMode(info)) {
return record;
}
}
return null;
}
+ private Display.ColorTransform findColorTransform(SurfaceControl.PhysicalDisplayInfo info) {
+ for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
+ Display.ColorTransform transform = mSupportedColorTransforms.valueAt(i);
+ if (transform.getColorTransform() == info.colorTransform) {
+ return transform;
+ }
+ }
+ return null;
+ }
+
@Override
public void applyPendingDisplayDeviceInfoChangesLocked() {
if (mHavePendingChanges) {
@@ -247,7 +351,7 @@
@Override
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
if (mInfo == null) {
- SurfaceControl.PhysicalDisplayInfo phys = mSupportedModes.get(mActiveModeId).mPhys;
+ SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex];
mInfo = new DisplayDeviceInfo();
mInfo.width = phys.width;
mInfo.height = phys.height;
@@ -258,6 +362,13 @@
DisplayModeRecord record = mSupportedModes.valueAt(i);
mInfo.supportedModes[i] = record.mMode;
}
+ mInfo.colorTransformId = mActiveColorTransformId;
+ mInfo.defaultColorTransformId = mDefaultColorTransformId;
+ mInfo.supportedColorTransforms =
+ new Display.ColorTransform[mSupportedColorTransforms.size()];
+ for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
+ mInfo.supportedColorTransforms[i] = mSupportedColorTransforms.valueAt(i);
+ }
mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos;
mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
mInfo.state = mState;
@@ -402,7 +513,8 @@
}
@Override
- public void requestModeInTransactionLocked(int modeId) {
+ public void requestColorTransformAndModeInTransactionLocked(
+ int colorTransformId, int modeId) {
if (modeId == 0) {
modeId = mDefaultModeId;
} else if (mSupportedModes.indexOfKey(modeId) < 0) {
@@ -410,13 +522,37 @@
+ " reverting to default display mode.");
modeId = mDefaultModeId;
}
- if (mActiveModeId == modeId && !mActiveModeInvalid) {
+
+ if (colorTransformId == 0) {
+ colorTransformId = mDefaultColorTransformId;
+ } else if (mSupportedColorTransforms.indexOfKey(colorTransformId) < 0) {
+ Slog.w(TAG, "Requested color transform " + colorTransformId + " is not supported"
+ + " by this display, reverting to the default color transform");
+ colorTransformId = mDefaultColorTransformId;
+ }
+ int physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
+ if (physIndex < 0) {
+ Slog.w(TAG, "Requested color transform, mode ID pair (" + colorTransformId + ", "
+ + modeId + ") not available, trying color transform with default mode ID");
+ modeId = mDefaultModeId;
+ physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
+ if (physIndex < 0) {
+ Slog.w(TAG, "Requested color transform with default mode ID still not"
+ + " available, falling back to default color transform with default"
+ + " mode.");
+ colorTransformId = mDefaultColorTransformId;
+ physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
+ }
+ }
+ if (mActivePhysIndex == physIndex) {
return;
}
- DisplayModeRecord record = mSupportedModes.get(modeId);
- SurfaceControl.setActiveConfig(getDisplayTokenLocked(), record.mPhysIndex);
+ SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
+ mActivePhysIndex = physIndex;
mActiveModeId = modeId;
mActiveModeInvalid = false;
+ mActiveColorTransformId = colorTransformId;
+ mActiveColorTransformInvalid = false;
updateDeviceInfoLocked();
}
@@ -424,10 +560,43 @@
public void dumpLocked(PrintWriter pw) {
super.dumpLocked(pw);
pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
+ pw.println("mActivePhysIndex=" + mActivePhysIndex);
pw.println("mActiveModeId=" + mActiveModeId);
+ pw.println("mActiveColorTransformId=" + mActiveColorTransformId);
pw.println("mState=" + Display.stateToString(mState));
pw.println("mBrightness=" + mBrightness);
pw.println("mBacklight=" + mBacklight);
+ pw.println("mDisplayInfos=");
+ for (int i = 0; i < mDisplayInfos.length; i++) {
+ pw.println(" " + mDisplayInfos[i]);
+ }
+ pw.println("mSupportedModes=");
+ for (int i = 0; i < mSupportedModes.size(); i++) {
+ pw.println(" " + mSupportedModes.valueAt(i));
+ }
+ pw.println("mSupportedColorTransforms=[");
+ for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
+ if (i != 0) {
+ pw.print(", ");
+ }
+ pw.print(mSupportedColorTransforms.valueAt(i));
+ }
+ pw.println("]");
+ }
+
+ private int findDisplayInfoIndexLocked(int colorTransformId, int modeId) {
+ DisplayModeRecord record = mSupportedModes.get(modeId);
+ Display.ColorTransform transform = mSupportedColorTransforms.get(colorTransformId);
+ if (record != null && transform != null) {
+ for (int i = 0; i < mDisplayInfos.length; i++) {
+ SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i];
+ if (info.colorTransform == transform.getColorTransform()
+ && record.hasMatchingMode(info)){
+ return i;
+ }
+ }
+ }
+ return -1;
}
private void updateDeviceInfoLocked() {
@@ -441,13 +610,28 @@
*/
private static final class DisplayModeRecord {
public final Display.Mode mMode;
- public final SurfaceControl.PhysicalDisplayInfo mPhys;
- public int mPhysIndex;
- public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys, int physIndex) {
+ public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) {
mMode = createMode(phys.width, phys.height, phys.refreshRate);
- mPhys = phys;
- mPhysIndex = physIndex;
+ }
+
+ /**
+ * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode
+ * contained by the record modulo mode ID.
+ *
+ * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just
+ * that they generate identical modes.
+ */
+ public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) {
+ int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
+ int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate);
+ return mMode.getPhysicalWidth() == info.width
+ && mMode.getPhysicalHeight() == info.height
+ && modeRefreshRate == displayInfoRefreshRate;
+ }
+
+ public String toString() {
+ return "DisplayModeRecord{mMode=" + mMode + "}";
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 6efc99a..6dae397 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -74,6 +74,7 @@
private boolean mHasContent;
private int mRequestedModeId;
+ private int mRequestedColorTransformId;
// The display offsets to apply to the display projection.
private int mDisplayOffsetX;
@@ -235,6 +236,11 @@
mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
mBaseDisplayInfo.supportedModes = Arrays.copyOf(
deviceInfo.supportedModes, deviceInfo.supportedModes.length);
+ mBaseDisplayInfo.colorTransformId = deviceInfo.colorTransformId;
+ mBaseDisplayInfo.defaultColorTransformId = deviceInfo.defaultColorTransformId;
+ mBaseDisplayInfo.supportedColorTransforms = Arrays.copyOf(
+ deviceInfo.supportedColorTransforms,
+ deviceInfo.supportedColorTransforms.length);
mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
@@ -275,11 +281,12 @@
// Set the layer stack.
device.setLayerStackInTransactionLocked(isBlanked ? BLANK_LAYER_STACK : mLayerStack);
- // Set the mode.
+ // Set the color transform and mode.
if (device == mPrimaryDisplayDevice) {
- device.requestModeInTransactionLocked(mRequestedModeId);
+ device.requestColorTransformAndModeInTransactionLocked(
+ mRequestedColorTransformId, mRequestedModeId);
} else {
- device.requestModeInTransactionLocked(0); // Revert to default.
+ device.requestColorTransformAndModeInTransactionLocked(0, 0); // Revert to default.
}
// Only grab the display info now as it may have been changed based on the requests above.
@@ -383,6 +390,18 @@
}
/**
+ * Requests the given color transform.
+ */
+ public void setRequestedColorTransformIdLocked(int colorTransformId) {
+ mRequestedColorTransformId = colorTransformId;
+ }
+
+ /** Returns the pending requested color transform. */
+ public int getRequestedColorTransformIdLocked() {
+ return mRequestedColorTransformId;
+ }
+
+ /**
* Gets the burn-in offset in X.
*/
public int getDisplayOffsetXLocked() {
@@ -409,6 +428,7 @@
pw.println("mLayerStack=" + mLayerStack);
pw.println("mHasContent=" + mHasContent);
pw.println("mRequestedMode=" + mRequestedModeId);
+ pw.println("mRequestedColorTransformId=" + mRequestedColorTransformId);
pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")");
pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
mPrimaryDisplayDevice.getNameLocked() : "null"));
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 0bddff0..cf6264a 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -310,7 +310,7 @@
}
@Override
- public void requestModeInTransactionLocked(int id) {
+ public void requestColorTransformAndModeInTransactionLocked(int color, int id) {
int index = -1;
if (id == 0) {
// Use the default.
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index ea7d85e..ec7c1c4 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -337,7 +337,7 @@
return;
}
stopPendingOperations(true);
- mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted);
+ mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted, token.toString());
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
final int result = daemon.enroll(cryptoToken, groupId, timeout);
@@ -417,14 +417,15 @@
}
void startAuthentication(IBinder token, long opId, int groupId,
- IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
+ IFingerprintServiceReceiver receiver, int flags, boolean restricted,
+ String opPackageName) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startAuthentication: no fingeprintd!");
return;
}
stopPendingOperations(true);
- mAuthClient = new ClientMonitor(token, receiver, groupId, restricted);
+ mAuthClient = new ClientMonitor(token, receiver, groupId, restricted, opPackageName);
if (inLockoutMode()) {
Slog.v(TAG, "In lockout mode; disallowing authentication");
if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
@@ -481,7 +482,7 @@
}
stopPendingOperations(true);
- mRemoveClient = new ClientMonitor(token, receiver, userId, restricted);
+ mRemoveClient = new ClientMonitor(token, receiver, userId, restricted, token.toString());
// The fingerprint template ids will be removed when we get confirmation from the HAL
try {
final int result = daemon.remove(fingerId, userId);
@@ -574,11 +575,11 @@
}
if (mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, uid, opPackageName)
!= AppOpsManager.MODE_ALLOWED) {
- Slog.v(TAG, "Rejecting " + opPackageName + " ; permission denied");
+ Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
return false;
}
if (foregroundOnly && !isForegroundActivity(uid, pid)) {
- Slog.v(TAG, "Rejecting " + opPackageName + " ; not in foreground");
+ Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
return false;
}
return true;
@@ -606,13 +607,15 @@
IFingerprintServiceReceiver receiver;
int userId;
boolean restricted; // True if client does not have MANAGE_FINGERPRINT permission
+ String owner;
public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId,
- boolean restricted) {
+ boolean restricted, String owner) {
this.token = token;
this.receiver = receiver;
this.userId = userId;
this.restricted = restricted;
+ this.owner = owner; // name of the client that owns this - for debugging
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -695,6 +698,10 @@
if (!authenticated) {
receiver.onAuthenticationFailed(mHalDeviceId);
} else {
+ if (DEBUG) {
+ Slog.v(TAG, "onAuthenticated(owner=" + mAuthClient.owner
+ + ", id=" + fpId + ", gp=" + groupId + ")");
+ }
Fingerprint fp = !restricted ?
new Fingerprint("" /* TODO */, groupId, fpId, mHalDeviceId) : null;
receiver.onAuthenticationSucceeded(mHalDeviceId, fp);
@@ -915,6 +922,7 @@
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
if (!canUseFingerprint(opPackageName, true /* foregroundOnly */)) {
+ if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
return;
}
@@ -927,7 +935,8 @@
@Override
public void run() {
MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
- startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted);
+ startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted,
+ opPackageName);
}
});
}
diff --git a/services/core/java/com/android/server/firewall/SenderPackageFilter.java b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
index ec9b5de..dc3edd9 100644
--- a/services/core/java/com/android/server/firewall/SenderPackageFilter.java
+++ b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
@@ -44,7 +44,9 @@
int packageUid = -1;
try {
- packageUid = pm.getPackageUid(mPackageName, UserHandle.USER_OWNER);
+ // USER_SYSTEM here is not important. Only app id is used and getPackageUid() will
+ // return a uid whether the app is installed for a user or not.
+ packageUid = pm.getPackageUid(mPackageName, UserHandle.USER_SYSTEM);
} catch (RemoteException ex) {
// handled below
}
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index d1bb8db..b3a0010 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -94,7 +94,7 @@
private void bindHardwareGeofence() {
mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
- mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.OWNER);
+ mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 5e5a55c..bc9f520 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -22,8 +22,6 @@
import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
import android.app.AlarmManager;
import android.app.AppOpsManager;
@@ -50,7 +48,10 @@
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkRequest;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.BatteryStats;
@@ -199,6 +200,8 @@
private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
private static final int SUBSCRIPTION_OR_SIM_CHANGED = 12;
private static final int INITIALIZE_HANDLER = 13;
+ private static final int REQUEST_SUPL_CONNECTION = 14;
+ private static final int RELEASE_SUPL_CONNECTION = 15;
// Request setid
private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
@@ -295,9 +298,6 @@
// true if we are enabled, protected by this
private boolean mEnabled;
- // true if we have network connectivity
- private boolean mNetworkAvailable;
-
// states for injecting ntp and downloading xtra data
private static final int STATE_PENDING_NETWORK = 0;
private static final int STATE_DOWNLOADING = 1;
@@ -372,10 +372,11 @@
// Handler for processing events
private Handler mHandler;
- private String mAGpsApn;
- private int mApnIpType;
+ /** It must be accessed only inside {@link #mHandler}. */
private int mAGpsDataConnectionState;
+ /** It must be accessed only inside {@link #mHandler}. */
private InetAddress mAGpsDataConnectionIpAddr;
+
private final ConnectivityManager mConnMgr;
private final GpsNetInitiatedHandler mNIHandler;
@@ -431,6 +432,42 @@
return mGpsNavigationMessageProvider;
}
+ /**
+ * Callback used to listen for data connectivity changes.
+ */
+ private final ConnectivityManager.NetworkCallback mNetworkConnectivityCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ requestUtcTime();
+ xtraDownloadRequest();
+ }
+ };
+
+ /**
+ * Callback used to listen for availability of a requested SUPL connection.
+ * It is kept as a separate instance from {@link #mNetworkConnectivityCallback} to be able to
+ * manage the registration/un-registration lifetimes separate.
+ */
+ private final ConnectivityManager.NetworkCallback mSuplConnectivityCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
+ }
+
+ @Override
+ public void onUnavailable() {
+ // timeout, it was not possible to establish the required connection
+ releaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
+ }
+ };
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -447,16 +484,6 @@
checkSmsSuplInit(intent);
} else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
checkWapSuplInit(intent);
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)
- || action.equals(ConnectivityManager.CONNECTIVITY_ACTION_SUPL)) {
- // retrieve NetworkType result for this UID
- int networkType = intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, -1);
- if (DEBUG) Log.d(TAG, "Connectivity action, type=" + networkType);
- if (networkType == ConnectivityManager.TYPE_MOBILE
- || networkType == ConnectivityManager.TYPE_WIFI
- || networkType == ConnectivityManager.TYPE_MOBILE_SUPL) {
- updateNetworkState(networkType);
- }
} else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
|| PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
|| Intent.ACTION_SCREEN_OFF.equals(action)
@@ -574,7 +601,7 @@
native_configuration_update(baos.toString());
Log.d(TAG, "final config = " + baos.toString());
} catch (IOException ex) {
- Log.w(TAG, "failed to dump properties contents");
+ Log.e(TAG, "failed to dump properties contents");
}
} else if (DEBUG) {
Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
@@ -740,78 +767,117 @@
return PROPERTIES;
}
- public void updateNetworkState(int networkType) {
- sendMessage(UPDATE_NETWORK_STATE, networkType, null /*obj*/);
- }
+ private void handleUpdateNetworkState(Network network) {
+ // retrieve NetworkInfo for this UID
+ NetworkInfo info = mConnMgr.getNetworkInfo(network);
+ if (info == null) {
+ return;
+ }
- private void handleUpdateNetworkState(int networkType) {
- ConnectivityManager connectivityManager = (ConnectivityManager) mContext
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo mobileInfo =
- connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
- NetworkInfo wifiInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
- mNetworkAvailable = (mobileInfo != null && mobileInfo.isConnected())
- || (wifiInfo != null && wifiInfo.isConnected());
- NetworkInfo info = connectivityManager.getNetworkInfo(networkType);
+ boolean isConnected = info.isConnected();
if (DEBUG) {
- Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
- + " info: " + info);
+ String message = String.format(
+ "UpdateNetworkState, state=%s, connected=%s, info=%s, capabilities=%S",
+ agpsDataConnStateAsString(),
+ isConnected,
+ info,
+ mConnMgr.getNetworkCapabilities(network));
+ Log.d(TAG, message);
}
- if (info != null) {
- if (native_is_agps_ril_supported()) {
- boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
- boolean networkAvailable = info.isAvailable() && dataEnabled;
- String defaultApn = getSelectedApn();
- if (defaultApn == null) {
- defaultApn = "dummy-apn";
- }
-
- native_update_network_state(info.isConnected(), info.getType(),
- info.isRoaming(), networkAvailable,
- info.getExtraInfo(), defaultApn);
- } else if (DEBUG) {
- Log.d(TAG, "Skipped network state update because AGPS-RIL in GPS HAL is not"
- + " supported");
+ if (native_is_agps_ril_supported()) {
+ boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
+ boolean networkAvailable = info.isAvailable() && dataEnabled;
+ String defaultApn = getSelectedApn();
+ if (defaultApn == null) {
+ defaultApn = "dummy-apn";
}
+
+ native_update_network_state(
+ isConnected,
+ info.getType(),
+ info.isRoaming(),
+ networkAvailable,
+ info.getExtraInfo(),
+ defaultApn);
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped network state update because GPS HAL AGPS-RIL is not supported");
}
- if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
- && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
- if (info.isConnected()) {
+ if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
+ if (isConnected) {
String apnName = info.getExtraInfo();
if (apnName == null) {
- /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
- exception in the following call to native_agps_data_conn_open*/
+ // assign a dummy value in the case of C2K as otherwise we will have a runtime
+ // exception in the following call to native_agps_data_conn_open
apnName = "dummy-apn";
}
- mAGpsApn = apnName;
- mApnIpType = getApnIpType(apnName);
+ int apnIpType = getApnIpType(apnName);
setRouting();
if (DEBUG) {
String message = String.format(
"native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
- mAGpsApn, mApnIpType);
+ apnName,
+ apnIpType);
Log.d(TAG, message);
}
- native_agps_data_conn_open(mAGpsApn, mApnIpType);
+ native_agps_data_conn_open(apnName, apnIpType);
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
} else {
- Log.e(TAG, "call native_agps_data_conn_failed, info: " + info);
- mAGpsApn = null;
- mApnIpType = APN_INVALID;
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
- native_agps_data_conn_failed();
+ handleReleaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
}
}
+ }
- if (mNetworkAvailable) {
- if (mInjectNtpTimePending == STATE_PENDING_NETWORK) {
- sendMessage(INJECT_NTP_TIME, 0, null);
- }
- if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
- sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
- }
+ private void handleRequestSuplConnection(InetAddress address) {
+ if (DEBUG) {
+ String message = String.format(
+ "requestSuplConnection, state=%s, address=%s",
+ agpsDataConnStateAsString(),
+ address);
+ Log.d(TAG, message);
+ }
+
+ if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
+ return;
+ }
+ mAGpsDataConnectionIpAddr = address;
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
+
+ NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+ requestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ requestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
+ NetworkRequest request = requestBuilder.build();
+ mConnMgr.requestNetwork(
+ request,
+ mSuplConnectivityCallback,
+ ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS);
+ }
+
+ private void handleReleaseSuplConnection(int agpsDataConnStatus) {
+ if (DEBUG) {
+ String message = String.format(
+ "releaseSuplConnection, state=%s, status=%s",
+ agpsDataConnStateAsString(),
+ agpsDataConnStatusAsString(agpsDataConnStatus));
+ Log.d(TAG, message);
+ }
+
+ if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) {
+ return;
+ }
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+
+ mConnMgr.unregisterNetworkCallback(mSuplConnectivityCallback);
+ switch (agpsDataConnStatus) {
+ case GPS_AGPS_DATA_CONN_FAILED:
+ native_agps_data_conn_failed();
+ break;
+ case GPS_RELEASE_AGPS_DATA_CONN:
+ native_agps_data_conn_closed();
+ break;
+ default:
+ Log.e(TAG, "Invalid status to release SUPL connection: " + agpsDataConnStatus);
}
}
@@ -820,7 +886,7 @@
// already downloading data
return;
}
- if (!mNetworkAvailable) {
+ if (!isDataNetworkConnected()) {
// try again when network is up
mInjectNtpTimePending = STATE_PENDING_NETWORK;
return;
@@ -888,7 +954,7 @@
// already downloading data
return;
}
- if (!mNetworkAvailable) {
+ if (!isDataNetworkConnected()) {
// try again when network is up
mDownloadXtraDataPending = STATE_PENDING_NETWORK;
return;
@@ -903,9 +969,7 @@
GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
byte[] data = xtraDownloader.downloadXtraData();
if (data != null) {
- if (DEBUG) {
- Log.d(TAG, "calling native_inject_xtra_data");
- }
+ if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
native_inject_xtra_data(data, data.length);
mXtraBackOff.reset();
}
@@ -1209,7 +1273,7 @@
if ("delete_aiding_data".equals(command)) {
result = deleteAidingData(extras);
} else if ("force_time_injection".equals(command)) {
- sendMessage(INJECT_NTP_TIME, 0, null);
+ requestUtcTime();
result = true;
} else if ("force_xtra_injection".equals(command)) {
if (mSupportsXtra) {
@@ -1536,51 +1600,20 @@
case GPS_REQUEST_AGPS_DATA_CONN:
if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
Log.v(TAG, "Received SUPL IP addr[]: " + ipaddr);
- // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
- // to avoid a race condition with handleUpdateNetworkState()
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
- int result = mConnMgr.startUsingNetworkFeature(
- ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+ InetAddress connectionIpAddress = null;
if (ipaddr != null) {
try {
- mAGpsDataConnectionIpAddr = InetAddress.getByAddress(ipaddr);
- Log.v(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr);
+ connectionIpAddress = InetAddress.getByAddress(ipaddr);
+ if (DEBUG) Log.d(TAG, "IP address converted to: " + connectionIpAddress);
} catch (UnknownHostException e) {
Log.e(TAG, "Bad IP Address: " + ipaddr, e);
- mAGpsDataConnectionIpAddr = null;
}
}
-
- if (result == PhoneConstants.APN_ALREADY_ACTIVE) {
- if (DEBUG) Log.d(TAG, "PhoneConstants.APN_ALREADY_ACTIVE");
- if (mAGpsApn != null) {
- setRouting();
- native_agps_data_conn_open(mAGpsApn, mApnIpType);
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
- } else {
- Log.e(TAG, "mAGpsApn not set when receiving PhoneConstants.APN_ALREADY_ACTIVE");
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
- native_agps_data_conn_failed();
- }
- } else if (result == PhoneConstants.APN_REQUEST_STARTED) {
- if (DEBUG) Log.d(TAG, "PhoneConstants.APN_REQUEST_STARTED");
- // Nothing to do here
- } else {
- if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed, value is " +
- result);
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
- native_agps_data_conn_failed();
- }
+ sendMessage(REQUEST_SUPL_CONNECTION, 0 /*arg*/, connectionIpAddress);
break;
case GPS_RELEASE_AGPS_DATA_CONN:
if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
- if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
- mConnMgr.stopUsingNetworkFeature(
- ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
- native_agps_data_conn_closed();
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
- mAGpsDataConnectionIpAddr = null;
- }
+ releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
break;
case GPS_AGPS_DATA_CONNECTED:
if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
@@ -1596,6 +1629,10 @@
}
}
+ private void releaseSuplConnection(int connStatus) {
+ sendMessage(RELEASE_SUPL_CONNECTION, connStatus, null /*obj*/);
+ }
+
/**
* called from native code to report NMEA data received
*/
@@ -1921,8 +1958,8 @@
/**
* Called from native code to request utc time info
*/
-
private void requestUtcTime() {
+ if (DEBUG) Log.d(TAG, "utcTimeRequest");
sendMessage(INJECT_NTP_TIME, 0, null);
}
@@ -1990,7 +2027,13 @@
handleSetRequest(gpsRequest.request, gpsRequest.source);
break;
case UPDATE_NETWORK_STATE:
- handleUpdateNetworkState(msg.arg1);
+ handleUpdateNetworkState((Network) msg.obj);
+ break;
+ case REQUEST_SUPL_CONNECTION:
+ handleRequestSuplConnection((InetAddress) msg.obj);
+ break;
+ case RELEASE_SUPL_CONNECTION:
+ handleReleaseSuplConnection(msg.arg1);
break;
case INJECT_NTP_TIME:
handleInjectNtpTime();
@@ -2007,13 +2050,13 @@
mDownloadXtraDataPending = STATE_IDLE;
break;
case UPDATE_LOCATION:
- handleUpdateLocation((Location)msg.obj);
+ handleUpdateLocation((Location) msg.obj);
break;
case SUBSCRIPTION_OR_SIM_CHANGED:
subscriptionOrSimChanged(mContext);
break;
case INITIALIZE_HANDLER:
- initialize();
+ handleInitialize();
break;
}
if (msg.arg2 == 1) {
@@ -2027,7 +2070,7 @@
* It is in charge of loading properties and registering for events that will be posted to
* this handler.
*/
- private void initialize() {
+ private void handleInitialize() {
// load default GPS configuration
// (this configuration might change in the future based on SIM changes)
reloadGpsProperties(mContext, mProperties);
@@ -2067,8 +2110,6 @@
intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
intentFilter.addAction(ALARM_TIMEOUT);
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -2076,6 +2117,14 @@
intentFilter.addAction(SIM_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+ // register for connectivity change events, this is equivalent to the deprecated way of
+ // registering for CONNECTIVITY_ACTION broadcasts
+ NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
+ networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ NetworkRequest networkRequest = networkRequestBuilder.build();
+ mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback);
+
// listen for PASSIVE_PROVIDER updates
LocationManager locManager =
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
@@ -2140,15 +2189,11 @@
}
private int getApnIpType(String apn) {
+ ensureInHandlerThread();
if (apn == null) {
return APN_INVALID;
}
- // look for cached data to use
- if (apn.equals(mAGpsApn) && mApnIpType != APN_INVALID) {
- return mApnIpType;
- }
-
String selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn);
Cursor cursor = null;
try {
@@ -2197,6 +2242,7 @@
return;
}
+ // TODO: replace the use of this deprecated API
boolean result = mConnMgr.requestRouteToHostAddress(
ConnectivityManager.TYPE_MOBILE_SUPL,
mAGpsDataConnectionIpAddr);
@@ -2208,6 +2254,61 @@
}
}
+ /**
+ * @return {@code true} if there is a data network available for outgoing connections,
+ * {@code false} otherwise.
+ */
+ private boolean isDataNetworkConnected() {
+ NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo();
+ return activeNetworkInfo != null && activeNetworkInfo.isConnected();
+ }
+
+ /**
+ * Ensures the calling function is running in the thread associated with {@link #mHandler}.
+ */
+ private void ensureInHandlerThread() {
+ if (mHandler != null && Looper.myLooper() == mHandler.getLooper()) {
+ return;
+ }
+ throw new RuntimeException("This method must run on the Handler thread.");
+ }
+
+ /**
+ * @return A string representing the current state stored in {@link #mAGpsDataConnectionState}.
+ */
+ private String agpsDataConnStateAsString() {
+ switch(mAGpsDataConnectionState) {
+ case AGPS_DATA_CONNECTION_CLOSED:
+ return "CLOSED";
+ case AGPS_DATA_CONNECTION_OPEN:
+ return "OPEN";
+ case AGPS_DATA_CONNECTION_OPENING:
+ return "OPENING";
+ default:
+ return "<Unknown>";
+ }
+ }
+
+ /**
+ * @return A string representing the given GPS_AGPS_DATA status.
+ */
+ private String agpsDataConnStatusAsString(int agpsDataConnStatus) {
+ switch (agpsDataConnStatus) {
+ case GPS_AGPS_DATA_CONNECTED:
+ return "CONNECTED";
+ case GPS_AGPS_DATA_CONN_DONE:
+ return "DONE";
+ case GPS_AGPS_DATA_CONN_FAILED:
+ return "FAILED";
+ case GPS_RELEASE_AGPS_DATA_CONN:
+ return "RELEASE";
+ case GPS_REQUEST_AGPS_DATA_CONN:
+ return "REQUEST";
+ default:
+ return "<Unknown>";
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
StringBuilder s = new StringBuilder();
@@ -2343,4 +2444,3 @@
// GNSS Configuration
private static native void native_configuration_update(String configData);
}
-
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0c884f15..f6c9374 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1642,6 +1642,10 @@
}
private void enforcePolicyAccess(String pkg, String method) {
+ if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
+ android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
+ return;
+ }
if (!checkPolicyAccess(pkg)) {
Slog.w(TAG, "Notification policy access denied calling " + method);
throw new SecurityException("Notification policy access denied");
@@ -1684,7 +1688,7 @@
public boolean matchesCallFilter(Bundle extras) {
enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
return mZenModeHelper.matchesCallFilter(
- UserHandle.getCallingUserHandle(),
+ Binder.getCallingUserHandle(),
extras,
mRankingHelper.findExtractor(ValidateNotificationPeople.class),
MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index cbe61c3..9131e5e 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -248,8 +248,9 @@
}
pw.printf("allow(calls=%s,callsFrom=%s,repeatCallers=%s,messages=%s,messagesFrom=%s,"
+ "events=%s,reminders=%s)\n",
- config.allowCalls, config.allowCallsFrom, config.allowRepeatCallers,
- config.allowMessages, config.allowMessagesFrom,
+ config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
+ config.allowRepeatCallers, config.allowMessages,
+ ZenModeConfig.sourceToString(config.allowMessagesFrom),
config.allowEvents, config.allowReminders);
pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule);
if (config.automaticRules.isEmpty()) return;
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 96a5e00..b504605 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -245,6 +245,8 @@
if (verifierPackage != null
&& doesPackageSupportRuntimePermissions(verifierPackage)) {
grantRuntimePermissionsLPw(verifierPackage, STORAGE_PERMISSIONS, true, userId);
+ grantRuntimePermissionsLPw(verifierPackage, PHONE_PERMISSIONS, false, userId);
+ grantRuntimePermissionsLPw(verifierPackage, SMS_PERMISSIONS, false, userId);
}
// SetupWizard
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b59b4b2..d867616 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -31,6 +31,21 @@
public final class Installer extends SystemService {
private static final String TAG = "Installer";
+ /* ***************************************************************************
+ * IMPORTANT: These values are passed to native code. Keep them in sync with
+ * frameworks/native/cmds/installd/installd.h
+ * **************************************************************************/
+ /** Application should be visible to everyone */
+ public static final int DEXOPT_PUBLIC = 1 << 1;
+ /** Application wants to run in VM safe mode */
+ public static final int DEXOPT_SAFEMODE = 1 << 2;
+ /** Application wants to allow debugging of its code */
+ public static final int DEXOPT_DEBUGGABLE = 1 << 3;
+ /** The system boot has finished */
+ public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
+ /** Run the application with the JIT compiler */
+ public static final int DEXOPT_USEJIT = 1 << 5;
+
private final InstallerConnection mInstaller;
public Installer(Context context) {
@@ -75,26 +90,24 @@
return mInstaller.execute(builder.toString());
}
- public int dexopt(String apkPath, int uid, boolean isPublic,
- String instructionSet, int dexoptNeeded) {
+ public int dexopt(String apkPath, int uid, String instructionSet,
+ int dexoptNeeded, int dexFlags) {
if (!isValidInstructionSet(instructionSet)) {
Slog.e(TAG, "Invalid instruction set: " + instructionSet);
return -1;
}
- return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet, dexoptNeeded);
+ return mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
}
- public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
- String instructionSet, int dexoptNeeded, boolean vmSafeMode,
- boolean debuggable, @Nullable String outputPath) {
+ public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+ int dexoptNeeded, @Nullable String outputPath, int dexFlags) {
if (!isValidInstructionSet(instructionSet)) {
Slog.e(TAG, "Invalid instruction set: " + instructionSet);
return -1;
}
- return mInstaller.dexopt(apkPath, uid, isPublic, pkgName,
- instructionSet, dexoptNeeded, vmSafeMode,
- debuggable, outputPath);
+ return mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
+ outputPath, dexFlags);
}
public int idmap(String targetApkPath, String overlayApkPath, int uid) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 7024ec8..6c6871f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -35,6 +35,11 @@
import dalvik.system.DexFile;
+import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
+import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
+import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
+import static com.android.server.pm.Installer.DEXOPT_USEJIT;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -71,7 +76,8 @@
* {@link PackageManagerService#mInstallLock}.
*/
int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
- boolean forceDex, boolean defer, boolean inclDependencies) {
+ boolean forceDex, boolean defer, boolean inclDependencies,
+ boolean bootComplete, boolean useJit) {
ArraySet<String> done;
if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
done = new ArraySet<String>();
@@ -86,7 +92,8 @@
mDexoptWakeLock.acquire();
}
try {
- return performDexOptLI(pkg, instructionSets, forceDex, defer, done);
+ return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete,
+ useJit, done);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -96,18 +103,20 @@
}
private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
- boolean forceDex, boolean defer, ArraySet<String> done) {
+ boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
+ ArraySet<String> done) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
if (done != null) {
done.add(pkg.packageName);
if (pkg.usesLibraries != null) {
- performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer, done);
+ performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer,
+ bootComplete, useJit, done);
}
if (pkg.usesOptionalLibraries != null) {
performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,
- done);
+ bootComplete, useJit, done);
}
}
@@ -174,11 +183,17 @@
Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
- + " oatDir = " + oatDir);
+ + " oatDir = " + oatDir + " bootComplete=" + bootComplete
+ + " useJit=" + useJit);
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ final int dexFlags =
+ (!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0)
+ | (vmSafeMode ? DEXOPT_SAFEMODE : 0)
+ | (debuggable ? DEXOPT_DEBUGGABLE : 0)
+ | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0)
+ | (useJit ? DEXOPT_USEJIT : 0);
final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
- !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,
- dexoptNeeded, vmSafeMode, debuggable, oatDir);
+ pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
// Dex2oat might fail due to compiler / verifier errors. We soldier on
// regardless, and attempt to interpret the app as a safety net.
@@ -217,8 +232,7 @@
@Nullable
private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
throws IOException {
- if ((pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) || pkg.isForwardLocked()
- || pkg.applicationInfo.isExternalAsec()) {
+ if (!pkg.canHaveOatDir()) {
return null;
}
File codePath = new File(pkg.codePath);
@@ -236,12 +250,13 @@
}
private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
- boolean forceDex, boolean defer, ArraySet<String> done) {
+ boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
+ ArraySet<String> done) {
for (String libName : libs) {
PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary(
libName);
if (libPkg != null && !done.contains(libName)) {
- performDexOptLI(libPkg, instructionSets, forceDex, defer, done);
+ performDexOptLI(libPkg, instructionSets, forceDex, defer, bootComplete, useJit, done);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 82afdd7..c729e28 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -75,6 +75,7 @@
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.ArrayUtils.appendInt;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -2015,7 +2016,8 @@
int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
alreadyDexOpted.add(lib);
- mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
+ mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
+ dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
@@ -2063,7 +2065,8 @@
try {
int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
+ mInstaller.dexopt(path, Process.SYSTEM_UID, dexCodeInstructionSet,
+ dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Jar not found: " + path);
@@ -2292,7 +2295,8 @@
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
- false /* force dexopt */, false /* defer dexopt */);
+ false /* force dexopt */, false /* defer dexopt */,
+ false /* boot complete */);
}
// Now that we know all the packages we are keeping,
@@ -4575,7 +4579,7 @@
// Check for results that need to skip the current profile.
ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
- if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
+ if (xpResolveInfo != null) {
List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
result.add(xpResolveInfo);
return filterIfNotSystemUser(result, userId);
@@ -4667,8 +4671,8 @@
int status = (int)(verificationState >> 32);
if (result == null) {
result = new CrossProfileDomainInfo();
- result.resolveInfo =
- createForwardingResolveInfo(new IntentFilter(), sourceUserId, parentUserId);
+ result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
+ sourceUserId, parentUserId);
result.bestDomainVerificationStatus = status;
} else {
result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
@@ -4922,8 +4926,8 @@
if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
// Checking if there are activities in the target user that can handle the
// intent.
- ResolveInfo resolveInfo = checkTargetCanHandle(filter, intent, resolvedType,
- flags, sourceUserId);
+ ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
+ resolvedType, flags, sourceUserId);
if (resolveInfo != null) {
return resolveInfo;
}
@@ -4950,8 +4954,8 @@
&& !alreadyTriedUserIds.get(targetUserId)) {
// Checking if there are activities in the target user that can handle the
// intent.
- ResolveInfo resolveInfo = checkTargetCanHandle(filter, intent, resolvedType,
- flags, sourceUserId);
+ ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
+ resolvedType, flags, sourceUserId);
if (resolveInfo != null) return resolveInfo;
alreadyTriedUserIds.put(targetUserId, true);
}
@@ -4960,17 +4964,24 @@
return null;
}
- private ResolveInfo checkTargetCanHandle(CrossProfileIntentFilter filter, Intent intent,
+ /**
+ * If the filter's target user can handle the intent and is enabled: returns a ResolveInfo that
+ * will forward the intent to the filter's target user.
+ * Otherwise, returns null.
+ */
+ private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, Intent intent,
String resolvedType, int flags, int sourceUserId) {
+ int targetUserId = filter.getTargetUserId();
List<ResolveInfo> resultTargetUser = mActivities.queryIntent(intent,
- resolvedType, flags, filter.getTargetUserId());
- if (resultTargetUser != null && !resultTargetUser.isEmpty()) {
- return createForwardingResolveInfo(filter, sourceUserId, filter.getTargetUserId());
+ resolvedType, flags, targetUserId);
+ if (resultTargetUser != null && !resultTargetUser.isEmpty()
+ && isUserEnabled(targetUserId)) {
+ return createForwardingResolveInfoUnchecked(filter, sourceUserId, targetUserId);
}
return null;
}
- private ResolveInfo createForwardingResolveInfo(IntentFilter filter,
+ private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
int sourceUserId, int targetUserId) {
ResolveInfo forwardingResolveInfo = new ResolveInfo();
long ident = Binder.clearCallingIdentity();
@@ -6148,41 +6159,6 @@
it.remove();
}
}
- // Give priority to system apps.
- for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
- PackageParser.Package pkg = it.next();
- if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding system app " + sortedPkgs.size() + ": " + pkg.packageName);
- }
- sortedPkgs.add(pkg);
- it.remove();
- }
- }
- // Give priority to updated system apps.
- for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
- PackageParser.Package pkg = it.next();
- if (pkg.isUpdatedSystemApp()) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding updated system app " + sortedPkgs.size() + ": " + pkg.packageName);
- }
- sortedPkgs.add(pkg);
- it.remove();
- }
- }
- // Give priority to apps that listen for boot complete.
- intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
- pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
- for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
- PackageParser.Package pkg = it.next();
- if (pkgNames.contains(pkg.packageName)) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding boot app " + sortedPkgs.size() + ": " + pkg.packageName);
- }
- sortedPkgs.add(pkg);
- it.remove();
- }
- }
// Filter out packages that aren't recently used.
filterRecentlyUsedApps(pkgs);
// Add all remaining apps.
@@ -6274,7 +6250,8 @@
PackageParser.Package p = pkg;
synchronized (mInstallLock) {
mPackageDexOptimizer.performDexOpt(p, null /* instruction sets */,
- false /* force dex */, false /* defer */, true /* include dependencies */);
+ false /* force dex */, false /* defer */, true /* include dependencies */,
+ false /* boot complete */, false /*useJit*/);
}
}
@@ -6333,7 +6310,8 @@
synchronized (mInstallLock) {
final String[] instructionSets = new String[] { targetInstructionSet };
int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
- false /* forceDex */, false /* defer */, true /* inclDependencies */);
+ false /* forceDex */, false /* defer */, true /* inclDependencies */,
+ true /* boot complete */, false /*useJit*/);
return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
}
} finally {
@@ -6383,7 +6361,8 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
- true /*forceDex*/, false /* defer */, true /* inclDependencies */);
+ true /*forceDex*/, false /* defer */, true /* inclDependencies */,
+ true /* boot complete */, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -7192,14 +7171,15 @@
// we can avoid redundant dexopts, and also to make sure we've got the
// code and package path correct.
adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
- pkg, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0);
+ pkg, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, true /* boot complete */);
}
if ((scanFlags & SCAN_NO_DEX) == 0) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
- forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */);
+ forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */,
+ (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
@@ -7279,7 +7259,8 @@
PackageParser.Package clientPkg = clientLibPkgs.get(i);
int result = mPackageDexOptimizer.performDexOpt(clientPkg,
null /* instruction sets */, forceDex,
- (scanFlags & SCAN_DEFER_DEX) != 0, false);
+ (scanFlags & SCAN_DEFER_DEX) != 0, false,
+ (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
"scanPackageLI failed to dexopt clientLibPkgs");
@@ -7835,7 +7816,8 @@
* adds unnecessary complexity.
*/
private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
- PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt) {
+ PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt,
+ boolean bootComplete) {
String requiredInstructionSet = null;
if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
requiredInstructionSet = VMRuntime.getInstructionSet(
@@ -7901,7 +7883,8 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
int result = mPackageDexOptimizer.performDexOpt(ps.pkg,
- null /* instruction sets */, forceDexOpt, deferDexOpt, true);
+ null /* instruction sets */, forceDexOpt, deferDexOpt, true,
+ bootComplete, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
@@ -12373,6 +12356,7 @@
// Retrieve PackageSettings and parse package
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+ | PackageParser.PARSE_ENFORCE_CODE
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
PackageParser pp = new PackageParser();
@@ -12590,8 +12574,8 @@
int result = mPackageDexOptimizer
.performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
- false /* defer */, false /* inclDependencies */);
-
+ false /* defer */, false /* inclDependencies */,
+ true /*bootComplete*/, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
@@ -13844,7 +13828,21 @@
// TODO(multiArch): Extend getSizeInfo to look at *all* instruction sets, not
// just the primary.
String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps));
- int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, p.baseCodePath,
+
+ String apkPath;
+ File packageDir = new File(p.codePath);
+
+ if (packageDir.isDirectory() && p.canHaveOatDir()) {
+ apkPath = packageDir.getAbsolutePath();
+ // If libDirRoot is inside a package dir, set it to null to avoid it being counted twice
+ if (libDirRoot != null && libDirRoot.startsWith(apkPath)) {
+ libDirRoot = null;
+ }
+ } else {
+ apkPath = p.baseCodePath;
+ }
+
+ int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, apkPath,
libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
if (res < 0) {
return false;
@@ -15880,14 +15878,28 @@
}
}
- private void loadPrivatePackages(VolumeInfo vol) {
+ private void loadPrivatePackages(final VolumeInfo vol) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ loadPrivatePackagesInner(vol);
+ }
+ });
+ }
+
+ private void loadPrivatePackagesInner(VolumeInfo vol) {
final ArrayList<ApplicationInfo> loaded = new ArrayList<>();
final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
- synchronized (mInstallLock) {
+
+ final VersionInfo ver;
+ final List<PackageSetting> packages;
synchronized (mPackages) {
- final VersionInfo ver = mSettings.findOrCreateVersion(vol.fsUuid);
- final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
- for (PackageSetting ps : packages) {
+ ver = mSettings.findOrCreateVersion(vol.fsUuid);
+ packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
+ }
+
+ for (PackageSetting ps : packages) {
+ synchronized (mInstallLock) {
final PackageParser.Package pkg;
try {
pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0L, null);
@@ -15900,7 +15912,9 @@
deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
}
}
+ }
+ synchronized (mPackages) {
int updateFlags = UPDATE_PERMISSIONS_ALL;
if (ver.sdkVersion != mSdkVersion) {
logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
@@ -15914,13 +15928,21 @@
mSettings.writeLPr();
}
- }
if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
sendResourcesChangedBroadcast(true, false, loaded, null);
}
- private void unloadPrivatePackages(VolumeInfo vol) {
+ private void unloadPrivatePackages(final VolumeInfo vol) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ unloadPrivatePackagesInner(vol);
+ }
+ });
+ }
+
+ private void unloadPrivatePackagesInner(VolumeInfo vol) {
final ArrayList<ApplicationInfo> unloaded = new ArrayList<>();
synchronized (mInstallLock) {
synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1924bab..de106a1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.accounts.Account;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityManager;
@@ -63,6 +64,7 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+import com.android.server.accounts.AccountManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -129,7 +131,7 @@
private static final int MIN_USER_ID = 10;
- private static final int USER_VERSION = 5;
+ private static final int USER_VERSION = 6;
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
@@ -406,6 +408,24 @@
}
}
+ @Override
+ public boolean canHaveRestrictedProfile(int userId) {
+ checkManageUsersPermission("canHaveRestrictedProfile");
+ synchronized (mPackagesLock) {
+ final UserInfo userInfo = getUserInfoLocked(userId);
+ if (userInfo == null || !userInfo.canHaveProfile()) {
+ return false;
+ }
+ if (!userInfo.isAdmin()) {
+ return false;
+ }
+ }
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ // restricted profile can be created if there is no DO set and the admin user has no PO
+ return dpm.getDeviceOwner() == null && dpm.getProfileOwnerAsUser(userId) == null;
+ }
+
/*
* Should be locked on mUsers before calling this.
*/
@@ -846,6 +866,20 @@
userVersion = 5;
}
+ if (userVersion < 6) {
+ final boolean splitSystemUser = UserManager.isSplitSystemUser();
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i);
+ // In non-split mode, only user 0 can have restricted profiles
+ if (!splitSystemUser && user.isRestricted()
+ && (user.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID)) {
+ user.restrictedProfileParentId = UserHandle.USER_SYSTEM;
+ scheduleWriteUserLocked(user);
+ }
+ }
+ userVersion = 6;
+ }
+
if (userVersion < USER_VERSION) {
Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
+ USER_VERSION);
@@ -1386,6 +1420,25 @@
}
/**
+ * @hide
+ */
+ public UserInfo createRestrictedProfile(String name, int parentUserId) {
+ checkManageUsersPermission("setupRestrictedProfile");
+ final UserInfo user = createProfileForUser(name, UserInfo.FLAG_RESTRICTED, parentUserId);
+ if (user == null) {
+ return null;
+ }
+ setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
+ // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
+ // the putIntForUser() will fail.
+ android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ android.provider.Settings.Secure.LOCATION_MODE,
+ android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
+ setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+ return user;
+ }
+
+ /**
* Find the current guest user. If the Guest user is partial,
* then do not include it in the results as it is about to die.
*/
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index 9095f57..051b7fb 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -258,7 +258,7 @@
vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile
}
if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 ||
- ((vis | oldVis) & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
+ ((vis | oldVis) & View.SYSTEM_UI_TRANSPARENT) != 0) {
mLastTranslucent = SystemClock.uptimeMillis();
}
return vis;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 80cb4e6..dbc3970 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -118,6 +118,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.widget.PointerLocationView;
+import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
@@ -126,7 +127,6 @@
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -266,9 +266,9 @@
WindowManagerFuncs mWindowManagerFuncs;
WindowManagerInternal mWindowManagerInternal;
PowerManager mPowerManager;
- PowerManagerInternal mPowerManagerInternal;
ActivityManagerInternal mActivityManagerInternal;
DreamManagerInternal mDreamManagerInternal;
+ PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
boolean mPreloadedRecentApps;
final Object mServiceAquireLock = new Object();
@@ -944,10 +944,17 @@
}
}
+ GestureLauncherService gestureService = LocalServices.getService(
+ GestureLauncherService.class);
+ boolean gesturedServiceIntercepted = false;
+ if (gestureService != null) {
+ gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive);
+ }
+
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
- || mScreenshotChordVolumeUpKeyTriggered;
+ || mScreenshotChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
if (!mPowerKeyHandled) {
if (interactive) {
// When interactive, we're already awake.
@@ -1336,8 +1343,8 @@
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
- mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
// Init display burn-in protection
boolean burnInProtectionEnabled = context.getResources().getBoolean(
@@ -1513,6 +1520,13 @@
}
}
@Override
+ public void onFling(int duration) {
+ if (mPowerManagerInternal != null) {
+ mPowerManagerInternal.powerHint(
+ PowerManagerInternal.POWER_HINT_INTERACTION, duration);
+ }
+ }
+ @Override
public void onDebug() {
// no-op
}
@@ -1989,6 +2003,7 @@
case TYPE_SYSTEM_DIALOG:
case TYPE_VOLUME_OVERLAY:
case TYPE_PRIVATE_PRESENTATION:
+ case TYPE_DOCK_DIVIDER:
break;
}
@@ -2120,54 +2135,56 @@
case TYPE_INPUT_METHOD_DIALOG:
// on-screen keyboards and other such input method user interfaces go here.
return 13;
+ case TYPE_DOCK_DIVIDER:
+ return 14;
case TYPE_KEYGUARD_SCRIM:
// the safety window that shows behind keyguard while keyguard is starting
- return 14;
- case TYPE_STATUS_BAR_SUB_PANEL:
return 15;
- case TYPE_STATUS_BAR:
+ case TYPE_STATUS_BAR_SUB_PANEL:
return 16;
- case TYPE_STATUS_BAR_PANEL:
+ case TYPE_STATUS_BAR:
return 17;
- case TYPE_KEYGUARD_DIALOG:
+ case TYPE_STATUS_BAR_PANEL:
return 18;
+ case TYPE_KEYGUARD_DIALOG:
+ return 19;
case TYPE_VOLUME_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
- return 19;
+ return 20;
case TYPE_SYSTEM_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
- return 20;
+ return 21;
case TYPE_NAVIGATION_BAR:
// the navigation bar, if available, shows atop most things
- return 21;
+ return 22;
case TYPE_NAVIGATION_BAR_PANEL:
// some panels (e.g. search) need to show on top of the navigation bar
- return 22;
+ return 23;
case TYPE_SYSTEM_ERROR:
// system-level error dialogs
- return 23;
+ return 24;
case TYPE_MAGNIFICATION_OVERLAY:
// used to highlight the magnified portion of a display
- return 24;
+ return 25;
case TYPE_DISPLAY_OVERLAY:
// used to simulate secondary display devices
- return 25;
+ return 26;
case TYPE_DRAG:
// the drag layer: input for drag-and-drop is associated with this window,
// which sits above all other focusable windows
- return 26;
+ return 27;
case TYPE_ACCESSIBILITY_OVERLAY:
// overlay put by accessibility services to intercept user interaction
- return 27;
- case TYPE_SECURE_SYSTEM_OVERLAY:
return 28;
- case TYPE_BOOT_PROGRESS:
+ case TYPE_SECURE_SYSTEM_OVERLAY:
return 29;
+ case TYPE_BOOT_PROGRESS:
+ return 30;
case TYPE_POINTER:
// the (mouse) pointer layer
- return 30;
+ return 31;
}
Log.e(TAG, "Unknown window type: " + type);
return 2;
@@ -2257,6 +2274,7 @@
case TYPE_WALLPAPER:
case TYPE_DREAM:
case TYPE_KEYGUARD_SCRIM:
+ case TYPE_DOCK_DIVIDER:
return false;
default:
// Hide only windows below the keyguard host window.
@@ -3563,7 +3581,6 @@
final Rect of = mTmpOverscanFrame;
final Rect vf = mTmpVisibleFrame;
final Rect dcf = mTmpDecorFrame;
- final Rect osf = mTmpOutsetFrame;
pf.left = df.left = of.left = vf.left = mDockLeft;
pf.top = df.top = of.top = vf.top = mDockTop;
pf.right = df.right = of.right = vf.right = mDockRight;
@@ -3605,152 +3622,165 @@
// then take that into account.
navVisible |= !canHideNavigationBar();
- boolean updateSysUiVisibility = false;
- if (mNavigationBar != null) {
- boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
- // Force the navigation bar to its appropriate place and
- // size. We need to do this directly, instead of relying on
- // it to bubble up from the nav bar, because this needs to
- // change atomically with screen rotations.
- mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
- if (mNavigationBarOnBottom) {
- // It's a system nav bar or a portrait screen; nav bar goes on bottom.
- int top = displayHeight - overscanBottom
- - mNavigationBarHeightForRotation[displayRotation];
- mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
- mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- mDockBottom = mTmpNavigationFrame.top;
- mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
- mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
- } else {
- // We currently want to hide the navigation UI.
- mNavigationBarController.setBarShowingLw(false);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the opaque nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemBottom = mTmpNavigationFrame.top;
- }
- } else {
- // Landscape screen; nav bar goes to the right.
- int left = displayWidth - overscanRight
- - mNavigationBarWidthForRotation[displayRotation];
- mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
- mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- mDockRight = mTmpNavigationFrame.left;
- mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
- mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
- } else {
- // We currently want to hide the navigation UI.
- mNavigationBarController.setBarShowingLw(false);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemRight = mTmpNavigationFrame.left;
- }
- }
- // Make sure the content and current rectangles are updated to
- // account for the restrictions from the navigation bar.
- mContentTop = mVoiceContentTop = mCurTop = mDockTop;
- mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
- mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
- mContentRight = mVoiceContentRight = mCurRight = mDockRight;
- mStatusBarLayer = mNavigationBar.getSurfaceLayer();
- // And compute the final frame.
- mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
- mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
- mTmpNavigationFrame, mTmpNavigationFrame);
- if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
- if (mNavigationBarController.checkHiddenLw()) {
- updateSysUiVisibility = true;
- }
- }
+ boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
+ displayRotation, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
+ navAllowedHidden);
if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
mDockLeft, mDockTop, mDockRight, mDockBottom));
-
- // decide where the status bar goes ahead of time
- if (mStatusBar != null) {
- // apply any navigation bar insets
- pf.left = df.left = of.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
- + mUnrestrictedScreenTop;
- vf.left = mStableLeft;
- vf.top = mStableTop;
- vf.right = mStableRight;
- vf.bottom = mStableBottom;
-
- mStatusBarLayer = mStatusBar.getSurfaceLayer();
-
- // Let the status bar determine its size.
- mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
- vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
- dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
-
- // For layout, the status bar is always at the top with our fixed height.
- mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
-
- boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
- boolean statusBarTranslucent = (sysui
- & (View.STATUS_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0;
- if (!isKeyguardShowing) {
- statusBarTranslucent &= areTranslucentBarsAllowed();
- }
-
- // If the status bar is hidden, we don't want to cause
- // windows behind it to scroll.
- if (mStatusBar.isVisibleLw() && !statusBarTransient) {
- // Status bar may go away, so the screen area it occupies
- // is available to apps but just covering them when the
- // status bar is visible.
- mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
-
- mContentTop = mVoiceContentTop = mCurTop = mDockTop;
- mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
- mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
- mContentRight = mVoiceContentRight = mCurRight = mDockRight;
-
- if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
- String.format(
- "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
- mDockLeft, mDockTop, mDockRight, mDockBottom,
- mContentLeft, mContentTop, mContentRight, mContentBottom,
- mCurLeft, mCurTop, mCurRight, mCurBottom));
- }
- if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()
- && !statusBarTransient && !statusBarTranslucent
- && !mStatusBarController.wasRecentlyTranslucent()) {
- // If the opaque status bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
- }
- if (mStatusBarController.checkHiddenLw()) {
- updateSysUiVisibility = true;
- }
- }
+ updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing);
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
}
}
+ private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui,
+ boolean isKeyguardShowing) {
+ // decide where the status bar goes ahead of time
+ if (mStatusBar != null) {
+ // apply any navigation bar insets
+ pf.left = df.left = of.left = mUnrestrictedScreenLeft;
+ pf.top = df.top = of.top = mUnrestrictedScreenTop;
+ pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
+ pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
+ + mUnrestrictedScreenTop;
+ vf.left = mStableLeft;
+ vf.top = mStableTop;
+ vf.right = mStableRight;
+ vf.bottom = mStableBottom;
+
+ mStatusBarLayer = mStatusBar.getSurfaceLayer();
+
+ // Let the status bar determine its size.
+ mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
+ vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
+ dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
+
+ // For layout, the status bar is always at the top with our fixed height.
+ mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
+
+ boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
+ boolean statusBarTranslucent = (sysui
+ & (View.STATUS_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0;
+ if (!isKeyguardShowing) {
+ statusBarTranslucent &= areTranslucentBarsAllowed();
+ }
+
+ // If the status bar is hidden, we don't want to cause
+ // windows behind it to scroll.
+ if (mStatusBar.isVisibleLw() && !statusBarTransient) {
+ // Status bar may go away, so the screen area it occupies
+ // is available to apps but just covering them when the
+ // status bar is visible.
+ mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
+
+ mContentTop = mVoiceContentTop = mCurTop = mDockTop;
+ mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
+ mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
+ mContentRight = mVoiceContentRight = mCurRight = mDockRight;
+
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
+ String.format(
+ "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
+ mDockLeft, mDockTop, mDockRight, mDockBottom,
+ mContentLeft, mContentTop, mContentRight, mContentBottom,
+ mCurLeft, mCurTop, mCurRight, mCurBottom));
+ }
+ if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()
+ && !statusBarTransient && !statusBarTranslucent
+ && !mStatusBarController.wasRecentlyTranslucent()) {
+ // If the opaque status bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
+ }
+ if (mStatusBarController.checkHiddenLw()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
+ int overscanRight, int overscanBottom, Rect dcf, boolean navVisible,
+ boolean navTranslucent, boolean navAllowedHidden) {
+ if (mNavigationBar != null) {
+ boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
+ // Force the navigation bar to its appropriate place and
+ // size. We need to do this directly, instead of relying on
+ // it to bubble up from the nav bar, because this needs to
+ // change atomically with screen rotations.
+ mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
+ if (mNavigationBarOnBottom) {
+ // It's a system nav bar or a portrait screen; nav bar goes on bottom.
+ int top = displayHeight - overscanBottom
+ - mNavigationBarHeightForRotation[displayRotation];
+ mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
+ mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ mDockBottom = mTmpNavigationFrame.top;
+ mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
+ mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
+ } else {
+ // We currently want to hide the navigation UI.
+ mNavigationBarController.setBarShowingLw(false);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the opaque nav bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemBottom = mTmpNavigationFrame.top;
+ }
+ } else {
+ // Landscape screen; nav bar goes to the right.
+ int left = displayWidth - overscanRight
+ - mNavigationBarWidthForRotation[displayRotation];
+ mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
+ mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ mDockRight = mTmpNavigationFrame.left;
+ mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
+ mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
+ } else {
+ // We currently want to hide the navigation UI.
+ mNavigationBarController.setBarShowingLw(false);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemRight = mTmpNavigationFrame.left;
+ }
+ }
+ // Make sure the content and current rectangles are updated to
+ // account for the restrictions from the navigation bar.
+ mContentTop = mVoiceContentTop = mCurTop = mDockTop;
+ mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
+ mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
+ mContentRight = mVoiceContentRight = mCurRight = mDockRight;
+ mStatusBarLayer = mNavigationBar.getSurfaceLayer();
+ // And compute the final frame.
+ mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
+ mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
+ mTmpNavigationFrame, mTmpNavigationFrame);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+ if (mNavigationBarController.checkHiddenLw()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/** {@inheritDoc} */
@Override
public int getSystemDecorLayerLw() {
@@ -4402,6 +4432,7 @@
if (attrs.type == TYPE_STATUS_BAR) {
if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
mForceStatusBarFromKeyguard = true;
+ mShowingLockscreen = true;
}
if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
mForceStatusBarTransparent = true;
@@ -4422,9 +4453,6 @@
mForceStatusBar = true;
}
}
- if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
- mShowingLockscreen = true;
- }
if (attrs.type == TYPE_DREAM) {
// If the lockscreen was showing when the dream started then wait
// for the dream to draw before hiding the lockscreen.
@@ -6107,6 +6135,7 @@
mKeyguardDelegate.bindService(mContext);
mKeyguardDelegate.onBootCompleted();
}
+ mSystemGestures.systemReady();
}
/** {@inheritDoc} */
@@ -7054,5 +7083,8 @@
if (mBurnInProtectionHelper != null) {
mBurnInProtectionHelper.dump(prefix, pw);
}
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.dump(prefix, pw);
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index 97831d1..4ae3154 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -17,9 +17,14 @@
package com.android.server.policy;
import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
import android.util.Slog;
+import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy.PointerEventListener;
+import android.widget.OverScroller;
/*
* Listens for system-wide input gestures, firing callbacks when detected.
@@ -31,12 +36,14 @@
private static final long SWIPE_TIMEOUT_MS = 500;
private static final int MAX_TRACKED_POINTERS = 32; // max per input system
private static final int UNTRACKED_POINTER = -1;
+ private static final int MAX_FLING_TIME_MILLIS = 5000;
private static final int SWIPE_NONE = 0;
private static final int SWIPE_FROM_TOP = 1;
private static final int SWIPE_FROM_BOTTOM = 2;
private static final int SWIPE_FROM_RIGHT = 3;
+ private final Context mContext;
private final int mSwipeStartThreshold;
private final int mSwipeDistanceThreshold;
private final Callbacks mCallbacks;
@@ -45,14 +52,19 @@
private final float[] mDownY = new float[MAX_TRACKED_POINTERS];
private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
+ private GestureDetector mGestureDetector;
+ private OverScroller mOverscroller;
+
int screenHeight;
int screenWidth;
private int mDownPointers;
private boolean mSwipeFireable;
private boolean mDebugFireable;
private boolean mMouseHoveringAtEdge;
+ private long mLastFlingTime;
public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
+ mContext = context;
mCallbacks = checkNull("callbacks", callbacks);
mSwipeStartThreshold = checkNull("context", context).getResources()
.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
@@ -68,8 +80,17 @@
return arg;
}
+ public void systemReady() {
+ Handler h = new Handler(Looper.myLooper());
+ mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h);
+ mOverscroller = new OverScroller(mContext);
+ }
+
@Override
public void onPointerEvent(MotionEvent event) {
+ if (mGestureDetector != null) {
+ mGestureDetector.onTouchEvent(event);
+ }
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mSwipeFireable = true;
@@ -211,10 +232,40 @@
return SWIPE_NONE;
}
+ private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ if (!mOverscroller.isFinished()) {
+ mOverscroller.forceFinished(true);
+ }
+ return true;
+ }
+ @Override
+ public boolean onFling(MotionEvent down, MotionEvent up,
+ float velocityX, float velocityY) {
+ mOverscroller.computeScrollOffset();
+ long now = SystemClock.uptimeMillis();
+
+ if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
+ mOverscroller.forceFinished(true);
+ }
+ mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY,
+ Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
+ int duration = mOverscroller.getDuration();
+ if (duration > MAX_FLING_TIME_MILLIS) {
+ duration = MAX_FLING_TIME_MILLIS;
+ }
+ mLastFlingTime = now;
+ mCallbacks.onFling(duration);
+ return true;
+ }
+ }
+
interface Callbacks {
void onSwipeFromTop();
void onSwipeFromBottom();
void onSwipeFromRight();
+ void onFling(int durationMs);
void onDown();
void onUpOrCancel();
void onMouseHoverAtTop();
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 6b45941..c0dfbcb 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -23,6 +23,8 @@
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardService;
+import java.io.PrintWriter;
+
/**
* A local class that keeps a cache of keyguard state that can be restored in the event
* keyguard crashes. It currently also allows runtime-selectable
@@ -127,7 +129,7 @@
intent.setComponent(keyguardComponent);
if (!context.bindServiceAsUser(intent, mKeyguardConnection,
- Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+ Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Log.v(TAG, "*** Keyguard: can't bind to " + keyguardComponent);
mKeyguardState.showing = false;
mKeyguardState.showingAndNotOccluded = false;
@@ -393,4 +395,26 @@
mKeyguardService.onActivityDrawn();
}
}
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + TAG);
+ prefix += " ";
+ pw.println(prefix + "showing=" + mKeyguardState.showing);
+ pw.println(prefix + "showingAndNotOccluded=" + mKeyguardState.showingAndNotOccluded);
+ pw.println(prefix + "inputRestricted=" + mKeyguardState.inputRestricted);
+ pw.println(prefix + "occluded=" + mKeyguardState.occluded);
+ pw.println(prefix + "secure=" + mKeyguardState.secure);
+ pw.println(prefix + "dreaming=" + mKeyguardState.dreaming);
+ pw.println(prefix + "systemIsReady=" + mKeyguardState.systemIsReady);
+ pw.println(prefix + "deviceHasKeyguard=" + mKeyguardState.deviceHasKeyguard);
+ pw.println(prefix + "enabled=" + mKeyguardState.enabled);
+ pw.println(prefix + "offReason=" + mKeyguardState.offReason);
+ pw.println(prefix + "currentUser=" + mKeyguardState.currentUser);
+ pw.println(prefix + "bootCompleted=" + mKeyguardState.bootCompleted);
+ pw.println(prefix + "screenState=" + mKeyguardState.screenState);
+ pw.println(prefix + "interactiveState=" + mKeyguardState.interactiveState);
+ if (mKeyguardService != null) {
+ mKeyguardService.dump(prefix, pw);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index cd88b66..429b188 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -27,6 +27,8 @@
import com.android.internal.policy.IKeyguardService;
import com.android.internal.policy.IKeyguardStateCallback;
+import java.io.PrintWriter;
+
/**
* A wrapper class for KeyguardService. It implements IKeyguardService to ensure the interface
* remains consistent.
@@ -239,4 +241,8 @@
public boolean isInputRestricted() {
return mKeyguardStateMonitor.isInputRestricted();
}
+
+ public void dump(String prefix, PrintWriter pw) {
+ mKeyguardStateMonitor.dump(prefix, pw);
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index f1f9c50..30cff03 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -25,6 +25,8 @@
import com.android.internal.policy.IKeyguardStateCallback;
import com.android.internal.widget.LockPatternUtils;
+import java.io.PrintWriter;
+
/**
* Maintains a cached copy of Keyguard's state.
* @hide
@@ -90,4 +92,13 @@
public void onInputRestrictedStateChanged(boolean inputRestricted) {
mInputRestricted = inputRestricted;
}
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + TAG);
+ prefix += " ";
+ pw.println(prefix + "mIsShowing=" + mIsShowing);
+ pw.println(prefix + "mSimSecure=" + mSimSecure);
+ pw.println(prefix + "mInputRestricted=" + mInputRestricted);
+ pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4e702aa..a0a31c0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -76,6 +76,7 @@
import libcore.util.Objects;
+import static android.os.PowerManagerInternal.POWER_HINT_INTERACTION;
import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
@@ -150,7 +151,6 @@
private static final int SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 5 * 1000;
// Power hints defined in hardware/libhardware/include/hardware/power.h.
- private static final int POWER_HINT_INTERACTION = 2;
private static final int POWER_HINT_LOW_POWER = 5;
// Power features defined in hardware/libhardware/include/hardware/power.h.
@@ -1577,6 +1577,13 @@
}
if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
+ if ((mUserActivitySummary &
+ (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
+ // Device is being kept awake by recent user activity
+ long savedWakeTimeMs = now - nextTimeout;
+ EventLog.writeEvent(
+ EventLogTags.POWER_SOFT_SLEEP_REQUESTED, savedWakeTimeMs);
+ }
mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
nextTimeout = -1;
}
@@ -3570,5 +3577,10 @@
public void uidGone(int uid) {
uidGoneInternal(uid);
}
+
+ @Override
+ public void powerHint(int hintId, int data) {
+ powerHintInternal(hintId, data);
+ }
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 5d01931..25d646d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -28,5 +28,5 @@
void showScreenPinningRequest();
void showAssistDisclosure();
void startAssist(Bundle args);
- void onCameraLaunchGestureDetected();
+ void onCameraLaunchGestureDetected(int source);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 87dc6c4..19b03d5 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -178,10 +178,10 @@
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
if (mBar != null) {
try {
- mBar.onCameraLaunchGestureDetected();
+ mBar.onCameraLaunchGestureDetected(source);
} catch (RemoteException e) {
}
}
@@ -305,9 +305,8 @@
throw new SecurityException("invalid status bar icon slot: " + slot);
}
- StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.OWNER, iconId,
- iconLevel, 0,
- contentDescription);
+ StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
+ iconLevel, 0, contentDescription);
//Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
mIcons.setIcon(index, icon);
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index f4bd61f..6c5452a 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -188,7 +188,7 @@
| Context.BIND_AUTO_CREATE;
// Bind to Telecom and register the service
- if (mContext.bindServiceAsUser(intent, serviceConnection, flags, UserHandle.OWNER)) {
+ if (mContext.bindServiceAsUser(intent, serviceConnection, flags, UserHandle.SYSTEM)) {
mServiceConnection = serviceConnection;
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index ee2353f..d814ebf 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -445,14 +445,16 @@
// Unregister all callbacks and unbind all services.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (serviceState.callback != null) {
- try {
- serviceState.service.unregisterCallback(serviceState.callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in unregisterCallback", e);
+ if (serviceState.service != null) {
+ if (serviceState.callback != null) {
+ try {
+ serviceState.service.unregisterCallback(serviceState.callback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ }
}
+ mContext.unbindService(serviceState.connection);
}
- mContext.unbindService(serviceState.connection);
}
userState.serviceStateMap.clear();
}
@@ -1974,7 +1976,12 @@
Slog.d(TAG, "onServiceConnected(component=" + component + ")");
}
synchronized (mLock) {
- UserState userState = getOrCreateUserStateLocked(mUserId);
+ UserState userState = mUserStates.get(mUserId);
+ if (userState == null) {
+ // The user was removed while connecting.
+ mContext.unbindService(this);
+ return;
+ }
ServiceState serviceState = userState.serviceStateMap.get(mComponent);
serviceState.service = ITvInputService.Stub.asInterface(service);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 192b168..a64cda6 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1003,17 +1003,33 @@
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
}
- private Animation createRelaunchAnimation(int appWidth, int appHeight) {
+ private Animation createRelaunchAnimation(int appWidth, int appHeight,
+ Rect containingFrame) {
getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
final int left = mTmpFromClipRect.left;
final int top = mTmpFromClipRect.top;
mTmpFromClipRect.offset(-left, -top);
mTmpToClipRect.set(0, 0, appWidth, appHeight);
AnimationSet set = new AnimationSet(true);
- ClipRectAnimation clip = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- TranslateAnimation translate = new TranslateAnimation(left, 0, top, 0);
- clip.setInterpolator(mDecelerateInterpolator);
- set.addAnimation(clip);
+ float fromWidth = mTmpFromClipRect.width();
+ float toWidth = mTmpToClipRect.width();
+ float fromHeight = mTmpFromClipRect.height();
+ float toHeight = mTmpToClipRect.height();
+ if (fromWidth <= toWidth && fromHeight <= toHeight) {
+ // The final window is larger in both dimensions than current window (e.g. we are
+ // maximizing), so we can simply unclip the new window and there will be no disappearing
+ // frame.
+ set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect));
+ } else {
+ // The disappearing window has one larger dimension. We need to apply scaling, so the
+ // first frame of the entry animation matches the old window.
+ set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
+ }
+
+ // We might not be going exactly full screen, but instead be aligned under the status bar.
+ // We need to take this into account when creating the translate animation.
+ TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
+ 0, top - containingFrame.top, 0);
set.addAnimation(translate);
set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
return set;
@@ -1056,7 +1072,12 @@
+ " anim=" + a + " transit=" + appTransitionToString(transit)
+ " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
} else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
- a = createRelaunchAnimation(appWidth, appHeight);
+ a = createRelaunchAnimation(appWidth, appHeight, containingFrame);
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+ "applyAnimation:"
+ + " anim=" + a + " nextAppTransition=" + mNextAppTransition
+ + " transit=" + appTransitionToString(transit)
+ + " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimationRes(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
@@ -1077,6 +1098,7 @@
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
+ + " transit=" + appTransitionToString(transit)
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 4f62909..5a1ed58 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -74,10 +74,6 @@
// case do not clear allDrawn until the animation completes.
boolean deferClearAllDrawn;
- // Is this token going to be hidden in a little while? If so, it
- // won't be taken into account for setting the screen orientation.
- boolean willBeHidden;
-
// Is this window's surface needed? This is almost like hidden, except
// it will sometimes be true a little earlier: when the token has
// been shown, but is still waiting for its app transition to execute
@@ -112,6 +108,9 @@
boolean mLaunchTaskBehind;
boolean mEnteringAnimation;
+ // True if the windows associated with this token should be cropped to their stack bounds.
+ boolean mCropWindowsToStack;
+
// This application will have its window replaced due to relaunch. This allows window manager
// to differentiate between simple removal of a window and replacement. In the latter case it
// will preserve the old window until the new one is drawn.
@@ -318,7 +317,6 @@
pw.print(" requestedOrientation="); pw.println(requestedOrientation);
pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
pw.print(" clientHidden="); pw.print(clientHidden);
- pw.print(" willBeHidden="); pw.print(willBeHidden);
pw.print(" reportedDrawn="); pw.print(reportedDrawn);
pw.print(" reportedVisible="); pw.println(reportedVisible);
if (paused) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 99e9fe6..d6d77e9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,13 +16,17 @@
package com.android.server.wm;
+import static android.app.ActivityManager.DOCKED_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerService.TAG;
+import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
+import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
import android.graphics.Rect;
import android.graphics.Region;
+import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
@@ -70,6 +74,7 @@
boolean mDisplayScalingDisabled;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Display mDisplay;
+ private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Rect mBaseDisplayRect = new Rect();
Rect mContentRect = new Rect();
@@ -80,11 +85,11 @@
final boolean isDefaultDisplay;
/** Window tokens that are in the process of exiting, but still on screen for animations. */
- final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>();
+ final ArrayList<WindowToken> mExitingTokens = new ArrayList<>();
/** Array containing all TaskStacks on this display. Array
* is stored in display order with the current bottom stack at 0. */
- private final ArrayList<TaskStack> mStacks = new ArrayList<TaskStack>();
+ private final ArrayList<TaskStack> mStacks = new ArrayList<>();
/** A special TaskStack with id==HOME_STACK_ID that moves to the bottom whenever any TaskStack
* (except a future lockscreen TaskStack) moves to the top. */
@@ -97,7 +102,8 @@
Region mTouchExcludeRegion = new Region();
/** Save allocating when calculating rects */
- Rect mTmpRect = new Rect();
+ private Rect mTmpRect = new Rect();
+ private Rect mTmpRect2 = new Rect();
/** For gathering Task objects in order. */
final ArrayList<Task> mTmpTaskHistory = new ArrayList<Task>();
@@ -107,6 +113,8 @@
/** Remove this display when animation on it has completed. */
boolean mDeferredRemoval;
+ final DockedStackDividerController mDividerControllerLocked;
+
/**
* @param display May not be null.
* @param service You know.
@@ -115,8 +123,10 @@
mDisplay = display;
mDisplayId = display.getDisplayId();
display.getDisplayInfo(mDisplayInfo);
+ display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
mService = service;
+ mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
}
int getDisplayId() {
@@ -135,6 +145,10 @@
return mDisplayInfo;
}
+ DisplayMetrics getDisplayMetrics() {
+ return mDisplayMetrics;
+ }
+
/**
* Returns true if the specified UID has access to this display.
*/
@@ -172,6 +186,7 @@
void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
+ mDisplay.getMetrics(mDisplayMetrics);
for (int i = mStacks.size() - 1; i >= 0; --i) {
mStacks.get(i).updateDisplayInfo(null);
}
@@ -240,17 +255,87 @@
return -1;
}
+ /**
+ * Find the window whose outside touch area (for resizing) (x, y) falls within.
+ * Returns null if the touch doesn't fall into a resizing area.
+ */
+ WindowState findWindowForControlPoint(int x, int y) {
+ final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ TaskStack stack = mStacks.get(stackNdx);
+ if (!stack.allowTaskResize()) {
+ break;
+ }
+ final ArrayList<Task> tasks = stack.getTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final Task task = tasks.get(taskNdx);
+ if (task.isFullscreen()) {
+ return null;
+ }
+
+ // We need to use the visible frame on the window for any touch-related
+ // tests. Can't use the task's bounds because the original task bounds
+ // might be adjusted to fit the content frame. (One example is when the
+ // task is put to top-left quadrant, the actual visible frame would not
+ // start at (0,0) after it's adjusted for the status bar.)
+ WindowState win = task.getTopAppMainWindow();
+ if (win != null) {
+ win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+ mTmpRect.inset(-delta, -delta);
+ if (mTmpRect.contains(x, y)) {
+ mTmpRect.inset(delta, delta);
+ if (!mTmpRect.contains(x, y)) {
+ return win;
+ }
+ // User touched inside the task. No need to look further,
+ // focus transfer will be handled in ACTION_UP.
+ return null;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
void setTouchExcludeRegion(Task focusedTask) {
mTouchExcludeRegion.set(mBaseDisplayRect);
WindowList windows = getWindowList();
+ final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
for (int i = windows.size() - 1; i >= 0; --i) {
final WindowState win = windows.get(i);
final Task task = win.getTask();
- if (win.isVisibleLw() && task != null && task != focusedTask) {
- mTmpRect.set(win.mVisibleFrame);
- // If no intersection, we need mTmpRect to be unmodified.
- mTmpRect.intersect(win.mVisibleInsets);
- mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+ if (win.isVisibleLw() && task != null) {
+ /**
+ * Exclusion region is the region that TapDetector doesn't care about.
+ * Here we want to remove all non-focused tasks from the exclusion region.
+ * We also remove the outside touch area for resizing for all freeform
+ * tasks (including the focused).
+ *
+ * (For freeform focused task, the below logic will first remove the enlarged
+ * area, then add back the inner area.)
+ */
+ final boolean isFreeformed = task.inFreeformWorkspace();
+ if (task != focusedTask || isFreeformed) {
+ mTmpRect.set(win.mVisibleFrame);
+ mTmpRect.intersect(win.mVisibleInsets);
+ /**
+ * If the task is freeformed, enlarge the area to account for outside
+ * touch area for resize.
+ */
+ if (isFreeformed) {
+ mTmpRect.inset(-delta, -delta);
+ }
+ mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+ }
+ /**
+ * If we removed the focused task above, add it back and only leave its
+ * outside touch area in the exclusion. TapDectector is not interested in
+ * any touch inside the focused task itself.
+ */
+ if (task == focusedTask && isFreeformed) {
+ mTmpRect.inset(delta, delta);
+ mTouchExcludeRegion.op(mTmpRect, Region.Op.UNION);
+ }
}
}
if (mTapDetector != null) {
@@ -372,6 +457,35 @@
}
}
+ void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
+ final int rotationDelta = DisplayContent.deltaRotation(oldRotation, newRotation);
+ getLogicalDisplayRect(mTmpRect);
+ switch (rotationDelta) {
+ case Surface.ROTATION_0:
+ mTmpRect2.set(bounds);
+ break;
+ case Surface.ROTATION_90:
+ mTmpRect2.top = mTmpRect.bottom - bounds.right;
+ mTmpRect2.left = bounds.top;
+ mTmpRect2.right = mTmpRect2.left + bounds.height();
+ mTmpRect2.bottom = mTmpRect2.top + bounds.width();
+ break;
+ case Surface.ROTATION_180:
+ mTmpRect2.top = mTmpRect.bottom - bounds.bottom;
+ mTmpRect2.left = mTmpRect.right - bounds.right;
+ mTmpRect2.right = mTmpRect2.left + bounds.width();
+ mTmpRect2.bottom = mTmpRect2.top + bounds.height();
+ break;
+ case Surface.ROTATION_270:
+ mTmpRect2.top = bounds.left;
+ mTmpRect2.left = mTmpRect.right - bounds.bottom;
+ mTmpRect2.right = mTmpRect2.left + bounds.height();
+ mTmpRect2.bottom = mTmpRect2.top + bounds.width();
+ break;
+ }
+ bounds.set(mTmpRect2);
+ }
+
static int deltaRotation(int oldRotation, int newRotation) {
int delta = newRotation - oldRotation;
if (delta < 0) delta += 4;
@@ -452,4 +566,14 @@
public String toString() {
return "Display " + mDisplayId + " info=" + mDisplayInfo + " stacks=" + mStacks;
}
+
+ TaskStack getDockedStackLocked() {
+ for (int i = mStacks.size() - 1; i >= 0; i--) {
+ TaskStack stack = mStacks.get(i);
+ if (stack.mStackId == DOCKED_STACK_ID && stack.isVisibleLocked()) {
+ return stack;
+ }
+ }
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
new file mode 100644
index 0000000..8c5d319
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static com.android.server.wm.TaskStack.DOCKED_BOTTOM;
+import static com.android.server.wm.TaskStack.DOCKED_INVALID;
+import static com.android.server.wm.TaskStack.DOCKED_LEFT;
+import static com.android.server.wm.TaskStack.DOCKED_RIGHT;
+import static com.android.server.wm.TaskStack.DOCKED_TOP;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+
+/**
+ * Controls showing and hiding of a docked stack divider on the display.
+ */
+public class DockedStackDividerController implements View.OnTouchListener {
+ private static final String TAG = "DockedStackDivider";
+ private final Context mContext;
+ private final int mDividerWidth;
+ private final DisplayContent mDisplayContent;
+ private View mView;
+ private Rect mTmpRect = new Rect();
+ private Rect mLastResizeRect = new Rect();
+ private int mStartX;
+ private int mStartY;
+ private TaskStack mTaskStack;
+ private Rect mOriginalRect = new Rect();
+ private int mDockSide;
+
+
+ DockedStackDividerController(Context context, DisplayContent displayContent) {
+ mContext = context;
+ mDisplayContent = displayContent;
+ mDividerWidth = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ }
+
+ private void addDivider() {
+ View view = LayoutInflater.from(mContext).inflate(
+ com.android.internal.R.layout.docked_stack_divider, null);
+ view.setOnTouchListener(this);
+ WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ mDividerWidth, MATCH_PARENT, TYPE_DOCK_DIVIDER,
+ FLAG_TOUCHABLE_WHEN_WAKING | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
+ | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH,
+ PixelFormat.OPAQUE);
+ params.setTitle(TAG);
+ manager.addView(view, params, mDisplayContent.getDisplay(), null);
+ mView = view;
+ }
+
+ private void removeDivider() {
+ mView.setOnTouchListener(null);
+ WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
+ manager.removeView(mView, true /* immediate */);
+ mView = null;
+ }
+
+ boolean hasDivider() {
+ return mView != null;
+ }
+
+ void update() {
+ TaskStack stack = mDisplayContent.getDockedStackLocked();
+ if (stack != null && mView == null) {
+ addDivider();
+ } else if (stack == null && mView != null) {
+ removeDivider();
+ }
+ }
+
+ int getWidth() {
+ return mDividerWidth;
+ }
+
+ void positionDockedStackedDivider(Rect frame) {
+ TaskStack stack = mDisplayContent.getDockedStackLocked();
+ if (stack == null) {
+ // Unfortunately we might end up with still having a divider, even though the underlying
+ // stack was already removed. This is because we are on AM thread and the removal of the
+ // divider was deferred to WM thread and hasn't happened yet.
+ return;
+ }
+ final @TaskStack.DockSide int side = stack.getDockSide();
+ stack.getBounds(mTmpRect);
+ switch (side) {
+ case DOCKED_LEFT:
+ frame.set(mTmpRect.right, frame.top, mTmpRect.right + frame.width(), frame.bottom);
+ break;
+ case DOCKED_TOP:
+ frame.set(frame.left, mTmpRect.bottom, mTmpRect.right,
+ mTmpRect.bottom + frame.height());
+ break;
+ case DOCKED_RIGHT:
+ frame.set(mTmpRect.left - frame.width(), frame.top, mTmpRect.left, frame.bottom);
+ break;
+ case DOCKED_BOTTOM:
+ frame.set(frame.left, mTmpRect.top - frame.height(), frame.right, mTmpRect.top);
+ break;
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ // We use raw values, because getX/Y() would give us results relative to the
+ // dock divider bounds.
+ mStartX = (int) event.getRawX();
+ mStartY = (int) event.getRawY();
+ synchronized (mDisplayContent.mService.mWindowMap) {
+ mTaskStack = mDisplayContent.getDockedStackLocked();
+ mTaskStack.getBounds(mOriginalRect);
+ mDockSide = mTaskStack.getDockSide();
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mTaskStack != null) {
+ resizeStack(event);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mTaskStack = null;
+ mDockSide = TaskStack.DOCKED_INVALID;
+ break;
+ }
+ return true;
+ }
+
+ private void resizeStack(MotionEvent event) {
+ mTmpRect.set(mOriginalRect);
+ final int deltaX = (int) event.getRawX() - mStartX;
+ final int deltaY = (int) event.getRawY() - mStartY;
+ switch (mDockSide) {
+ case DOCKED_LEFT:
+ mTmpRect.right += deltaX;
+ break;
+ case DOCKED_TOP:
+ mTmpRect.bottom += deltaY;
+ break;
+ case DOCKED_RIGHT:
+ mTmpRect.left += deltaX;
+ break;
+ case DOCKED_BOTTOM:
+ mTmpRect.top += deltaY;
+ break;
+ }
+ if (mTmpRect.equals(mLastResizeRect)) {
+ return;
+ }
+ mLastResizeRect.set(mTmpRect);
+ try {
+ mDisplayContent.mService.mActivityManager.resizeStack(DOCKED_STACK_ID, mTmpRect);
+ } catch (RemoteException e) {
+ }
+ }
+
+ boolean isResizing() {
+ return mTaskStack != null;
+ }
+
+ int getWidthAdjustment() {
+ return getWidth() / 2;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index e87dcde..3521682 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -27,6 +27,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
@@ -34,6 +35,7 @@
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
+import android.view.DropPermissionHolder;
import android.view.InputChannel;
import android.view.SurfaceControl;
import android.view.View;
@@ -50,6 +52,7 @@
SurfaceControl mSurfaceControl;
int mFlags;
IBinder mLocalWin;
+ int mUid;
ClipData mData;
ClipDescription mDataDescription;
boolean mDragResult;
@@ -74,6 +77,7 @@
mSurfaceControl = surface;
mFlags = flags;
mLocalWin = localWin;
+ mUid = Binder.getCallingUid();
mNotifiedWindows = new ArrayList<WindowState>();
}
@@ -225,7 +229,7 @@
if (mDragInProgress && newWin.isPotentialDragTarget()) {
DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
- touchX, touchY, null, desc, null, false);
+ touchX, touchY, null, desc, null, null, false);
try {
newWin.mClient.dispatchDragEvent(event);
// track each window that we've notified that the drag is starting
@@ -265,7 +269,7 @@
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
}
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
- 0, 0, null, null, null, mDragResult);
+ 0, 0, null, null, null, null, mDragResult);
for (WindowState ws: mNotifiedWindows) {
try {
ws.mClient.dispatchDragEvent(evt);
@@ -283,11 +287,12 @@
// stop intercepting input
mService.mDragState.unregister();
- mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
// free our resources and drop all the object references
mService.mDragState.reset();
mService.mDragState = null;
+
+ mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
}
void notifyMoveLw(float x, float y) {
@@ -330,7 +335,7 @@
}
// force DRAG_EXITED_EVENT if appropriate
DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
- x, y, null, null, null, false);
+ x, y, null, null, null, null, false);
mTargetWindow.mClient.dispatchDragEvent(evt);
if (myPid != mTargetWindow.mSession.mPid) {
evt.recycle();
@@ -341,7 +346,7 @@
Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin);
}
DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
- x, y, null, null, null, false);
+ x, y, null, null, null, null, false);
touchedWin.mClient.dispatchDragEvent(evt);
if (myPid != touchedWin.mSession.mPid) {
evt.recycle();
@@ -353,11 +358,15 @@
mTargetWindow = touchedWin;
}
+ WindowState getDropTargetWinLw(float x, float y) {
+ return getTouchedWinAtPointLw(x, y);
+ }
+
// Tell the drop target about the data. Returns 'true' if we can immediately
// dispatch the global drag-ended message, 'false' if we need to wait for a
// result from the recipient.
- boolean notifyDropLw(float x, float y) {
- WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+ boolean notifyDropLw(WindowState touchedWin, DropPermissionHolder dropPermissionHolder,
+ float x, float y) {
if (touchedWin == null) {
// "drop" outside a valid window -- no recipient to apply a
// timeout to, and we can send the drag-ended message immediately.
@@ -371,7 +380,7 @@
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
- null, null, mData, false);
+ null, null, mData, dropPermissionHolder, false);
try {
touchedWin.mClient.dispatchDragEvent(evt);
@@ -414,7 +423,7 @@
continue;
}
- child.getTaskBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+ child.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
if (!mTmpRect.contains(x, y)) {
// outside of this window's activity stack == don't tell about drags
continue;
@@ -437,13 +446,16 @@
private static DragEvent obtainDragEvent(WindowState win, int action,
float x, float y, Object localState,
- ClipDescription description, ClipData data, boolean result) {
+ ClipDescription description, ClipData data,
+ DropPermissionHolder dropPermissionHolder,
+ boolean result) {
float winX = x - win.mFrame.left;
float winY = y - win.mFrame.top;
if (win.mEnforceSizeCompat) {
winX *= win.mGlobalScale;
winY *= win.mGlobalScale;
}
- return DragEvent.obtain(action, winX, winY, localState, description, data, result);
+ return DragEvent.obtain(action, winX, winY, localState, description, data,
+ dropPermissionHolder, result);
}
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b3244ff..6c391ad 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
import android.app.ActivityManagerNative;
import android.graphics.Rect;
@@ -64,9 +65,9 @@
public InputMonitor(WindowManagerService service) {
mService = service;
}
-
+
/* Notifies the window manager about a broken input channel.
- *
+ *
* Called by the InputManager.
*/
@Override
@@ -83,10 +84,10 @@
}
}
}
-
+
/* Notifies the window manager about an application that is not responding.
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
- *
+ *
* Called by the InputManager.
*/
@Override
@@ -170,7 +171,7 @@
private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,
final WindowState child, int flags, final int type, final boolean isVisible,
- final boolean hasFocus, final boolean hasWallpaper) {
+ final boolean hasFocus, final boolean hasWallpaper, DisplayContent displayContent) {
// Add a window to our list of input windows.
inputWindowHandle.name = child.toString();
final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -178,7 +179,7 @@
if (modal && child.mAppToken != null) {
// Limit the outer touch to the activity stack region.
flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- child.getTaskBounds(mTmpRect, BOUNDS_FOR_TOUCH);
+ child.getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
inputWindowHandle.touchableRegion.set(mTmpRect);
} else {
// Not modal or full screen modal
@@ -202,6 +203,20 @@
inputWindowHandle.frameTop = frame.top;
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
+ if (child.mAttrs.type == TYPE_DOCK_DIVIDER) {
+ // We need to determine if the divider is horizontal or vertical and adjust its handle
+ // frame accordingly.
+ int adjustment = displayContent.mDividerControllerLocked.getWidthAdjustment();
+ if (inputWindowHandle.frameRight - inputWindowHandle.frameLeft >
+ inputWindowHandle.frameTop - inputWindowHandle.frameBottom) {
+ // Horizontal divider.
+ inputWindowHandle.frameTop -= adjustment;
+ inputWindowHandle.frameBottom += adjustment;
+ } else {
+ inputWindowHandle.frameLeft -= adjustment;
+ inputWindowHandle.frameRight += adjustment;
+ }
+ }
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
@@ -257,13 +272,28 @@
}
}
+ final boolean inPositioning = (mService.mTaskPositioner != null);
+ if (inPositioning) {
+ if (WindowManagerService.DEBUG_TASK_POSITIONING) {
+ Log.d(WindowManagerService.TAG, "Inserting window handle for repositioning");
+ }
+ final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle;
+ if (dragWindowHandle != null) {
+ addInputWindowHandleLw(dragWindowHandle);
+ } else {
+ Slog.e(WindowManagerService.TAG,
+ "Repositioning is in progress but there is no drag window handle.");
+ }
+ }
+
boolean addInputConsumerHandle = mService.mInputConsumer != null;
// Add all windows on the default display.
final int numDisplays = mService.mDisplayContents.size();
final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
+ final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
+ final WindowList windows = displayContent.getWindowList();
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
final WindowState child = windows.get(winNdx);
final InputChannel inputChannel = child.mInputChannel;
@@ -301,7 +331,7 @@
}
addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
- hasWallpaper);
+ hasWallpaper, displayContent);
}
}
@@ -437,56 +467,56 @@
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
}
-
+
window.paused = true;
updateInputWindowsLw(true /*force*/);
}
}
-
+
public void resumeDispatchingLw(WindowToken window) {
if (window.paused) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
}
-
+
window.paused = false;
updateInputWindowsLw(true /*force*/);
}
}
-
+
public void freezeInputDispatchingLw() {
if (! mInputDispatchFrozen) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
}
-
+
mInputDispatchFrozen = true;
updateInputDispatchModeLw();
}
}
-
+
public void thawInputDispatchingLw() {
if (mInputDispatchFrozen) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
}
-
+
mInputDispatchFrozen = false;
updateInputDispatchModeLw();
}
}
-
+
public void setEventDispatchingLw(boolean enabled) {
if (mInputDispatchEnabled != enabled) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
}
-
+
mInputDispatchEnabled = enabled;
updateInputDispatchModeLw();
}
}
-
+
private void updateInputDispatchModeLw() {
mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 12f61f9..1f62bc1 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -347,6 +347,13 @@
return true; // success!
}
+ public boolean startMovingTask(IWindow window, float startX, float startY) {
+ if (WindowManagerService.DEBUG_TASK_POSITIONING) Slog.d(
+ WindowManagerService.TAG, "startMovingTask: {" + startX + "," + startY + "}");
+
+ return mService.startMovingTask(window, startX, startY);
+ }
+
public void reportDropResult(IWindow window, boolean consumed) {
IBinder token = window.asBinder();
if (WindowManagerService.DEBUG_DRAG) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 666d902..d467b4f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -16,9 +16,12 @@
package com.android.server.wm;
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import android.content.res.Configuration;
@@ -40,6 +43,13 @@
* when no window animation is driving it. */
private static final int DEFAULT_DIM_DURATION = 200;
+ // Return value from {@link setBounds} indicating no change was made to the Task bounds.
+ static final int BOUNDS_CHANGE_NONE = 0;
+ // Return value from {@link setBounds} indicating the position of the Task bounds changed.
+ static final int BOUNDS_CHANGE_POSITION = 1;
+ // Return value from {@link setBounds} indicating the size of the Task bounds changed.
+ static final int BOUNDS_CHANGE_SIZE = 1 << 1;
+
TaskStack mStack;
final AppTokenList mAppTokens = new AppTokenList();
final int mTaskId;
@@ -65,6 +75,9 @@
// For handling display rotations.
private Rect mTmpRect2 = new Rect();
+ // Whether the task is currently being drag-resized
+ private boolean mDragResizing;
+
// The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer.
WindowStateAnimator mDimWinAnimator;
// Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
@@ -165,7 +178,7 @@
}
/** Set the task bounds. Passing in null sets the bounds to fullscreen. */
- boolean setBounds(Rect bounds, Configuration config) {
+ int setBounds(Rect bounds, Configuration config) {
if (config == null) {
config = Configuration.EMPTY;
}
@@ -190,7 +203,7 @@
// ensure bounds are entirely within the display rect
if (!bounds.intersect(mTmpRect)) {
// Can't set bounds outside the containing display...Sorry!
- return false;
+ return BOUNDS_CHANGE_NONE;
}
}
mFullscreen = mTmpRect.equals(bounds);
@@ -199,16 +212,38 @@
if (bounds == null) {
// Can't set to fullscreen if we don't have a display to get bounds from...
- return false;
+ return BOUNDS_CHANGE_NONE;
}
if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
- return false;
+ return BOUNDS_CHANGE_NONE;
+ }
+
+ int boundsChange = BOUNDS_CHANGE_NONE;
+ if (mBounds.left != bounds.left || mBounds.right != bounds.right) {
+ boundsChange |= BOUNDS_CHANGE_POSITION;
+ }
+ if (mBounds.width() != bounds.width() || mBounds.height() != bounds.height()) {
+ boundsChange |= BOUNDS_CHANGE_SIZE;
}
mBounds.set(bounds);
mRotation = rotation;
updateDimLayer();
mOverrideConfig = mFullscreen ? Configuration.EMPTY : config;
+ return boundsChange;
+ }
+
+ boolean resizeLocked(Rect bounds, Configuration configuration, boolean forced) {
+ int boundsChanged = setBounds(bounds, configuration);
+ if (forced) {
+ boundsChanged |= BOUNDS_CHANGE_SIZE;
+ }
+ if (boundsChanged == BOUNDS_CHANGE_NONE) {
+ return false;
+ }
+ if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
+ resizeWindows();
+ }
return true;
}
@@ -216,6 +251,14 @@
out.set(mBounds);
}
+ void setDragResizing(boolean dragResizing) {
+ mDragResizing = dragResizing;
+ }
+
+ boolean isDragResizing() {
+ return mDragResizing;
+ }
+
void updateDisplayInfo(final DisplayContent displayContent) {
if (displayContent == null) {
return;
@@ -231,32 +274,15 @@
// Device rotation changed. We don't want the task to move around on the screen when
// this happens, so update the task bounds so it stays in the same place.
- final int rotationDelta = DisplayContent.deltaRotation(mRotation, newRotation);
- displayContent.getLogicalDisplayRect(mTmpRect);
- switch (rotationDelta) {
- case Surface.ROTATION_0:
- mTmpRect2.set(mBounds);
- break;
- case Surface.ROTATION_90:
- mTmpRect2.top = mTmpRect.bottom - mBounds.right;
- mTmpRect2.left = mBounds.top;
- mTmpRect2.right = mTmpRect2.left + mBounds.height();
- mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
- break;
- case Surface.ROTATION_180:
- mTmpRect2.top = mTmpRect.bottom - mBounds.bottom;
- mTmpRect2.left = mTmpRect.right - mBounds.right;
- mTmpRect2.right = mTmpRect2.left + mBounds.width();
- mTmpRect2.bottom = mTmpRect2.top + mBounds.height();
- break;
- case Surface.ROTATION_270:
- mTmpRect2.top = mBounds.left;
- mTmpRect2.left = mTmpRect.right - mBounds.bottom;
- mTmpRect2.right = mTmpRect2.left + mBounds.height();
- mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
- break;
+ mTmpRect2.set(mBounds);
+ displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
+ if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
+ // Post message to inform activity manager of the bounds change simulating
+ // a one-way call. We do this to prevent a deadlock between window manager
+ // lock and activity manager lock been held.
+ mService.mH.sendMessage(mService.mH.obtainMessage(
+ RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
}
- setBounds(mTmpRect2, mOverrideConfig);
}
/** Updates the dim layer bounds, recreating it if needed. */
@@ -411,6 +437,19 @@
return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
}
+ boolean inFreeformWorkspace() {
+ return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
+ boolean inDockedWorkspace() {
+ return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
+ }
+
+ WindowState getTopAppMainWindow() {
+ final int tokensCount = mAppTokens.size();
+ return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
+ }
+
@Override
public boolean isFullscreen() {
return mFullscreen;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
new file mode 100644
index 0000000..df2e5e8
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_FORCED;
+import static android.app.ActivityManager.RESIZE_MODE_USER;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
+
+import android.annotation.IntDef;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Looper;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.BatchedInputEventReceiver;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.input.InputWindowHandle;
+import com.android.server.wm.WindowManagerService.H;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+class TaskPositioner implements DimLayer.DimLayerUser {
+ private static final String TAG = "TaskPositioner";
+
+ // The margin the pointer position has to be within the side of the screen to be
+ // considered at the side of the screen.
+ private static final int SIDE_MARGIN_DIP = 100;
+
+ @IntDef(flag = true,
+ value = {
+ CTRL_NONE,
+ CTRL_LEFT,
+ CTRL_RIGHT,
+ CTRL_TOP,
+ CTRL_BOTTOM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CtrlType {}
+
+ private static final int CTRL_NONE = 0x0;
+ private static final int CTRL_LEFT = 0x1;
+ private static final int CTRL_RIGHT = 0x2;
+ private static final int CTRL_TOP = 0x4;
+ private static final int CTRL_BOTTOM = 0x8;
+
+ private final WindowManagerService mService;
+ private WindowPositionerEventReceiver mInputEventReceiver;
+ private Display mDisplay;
+ private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private DimLayer mDimLayer;
+ @CtrlType
+ private int mCurrentDimSide;
+ private Rect mTmpRect = new Rect();
+ private int mSideMargin;
+ private int mMinVisibleWidth;
+ private int mMinVisibleHeight;
+
+ private Task mTask;
+ private boolean mResizing;
+ private final Rect mWindowOriginalBounds = new Rect();
+ private final Rect mWindowDragBounds = new Rect();
+ private float mStartDragX;
+ private float mStartDragY;
+ @CtrlType
+ private int mCtrlType = CTRL_NONE;
+ private boolean mDragEnded = false;
+
+ InputChannel mServerChannel;
+ InputChannel mClientChannel;
+ InputApplicationHandle mDragApplicationHandle;
+ InputWindowHandle mDragWindowHandle;
+
+ private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
+ public WindowPositionerEventReceiver(
+ InputChannel inputChannel, Looper looper, Choreographer choreographer) {
+ super(inputChannel, looper, choreographer);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (!(event instanceof MotionEvent)
+ || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
+ return;
+ }
+ final MotionEvent motionEvent = (MotionEvent) event;
+ boolean handled = false;
+
+ try {
+ if (mDragEnded) {
+ // The drag has ended but the clean-up message has not been processed by
+ // window manager. Drop events that occur after this until window manager
+ // has a chance to clean-up the input handle.
+ handled = true;
+ return;
+ }
+
+ final float newX = motionEvent.getRawX();
+ final float newY = motionEvent.getRawY();
+
+ switch (motionEvent.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
+ }
+ } break;
+
+ case MotionEvent.ACTION_MOVE: {
+ if (DEBUG_TASK_POSITIONING){
+ Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
+ }
+ synchronized (mService.mWindowMap) {
+ mDragEnded = notifyMoveLocked(newX, newY);
+ }
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.TaskPositioner.resizeTask");
+ try {
+ mService.mActivityManager.resizeTask(
+ mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
+ } catch(RemoteException e) {}
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ } break;
+
+ case MotionEvent.ACTION_UP: {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
+ }
+ mDragEnded = true;
+ } break;
+
+ case MotionEvent.ACTION_CANCEL: {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
+ }
+ mDragEnded = true;
+ } break;
+ }
+
+ if (mDragEnded) {
+ synchronized (mService.mWindowMap) {
+ endDragLocked();
+ }
+ try {
+ if (mResizing) {
+ // We were using fullscreen surface during resizing. Request
+ // resizeTask() one last time to restore surface to window size.
+ mService.mActivityManager.resizeTask(
+ mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_FORCED);
+ }
+
+ if (mCurrentDimSide != CTRL_NONE) {
+ final int createMode = mCurrentDimSide == CTRL_LEFT
+ ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+ : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ mService.mActivityManager.moveTaskToDockedStack(
+ mTask.mTaskId, createMode, true /*toTop*/);
+ }
+ } catch(RemoteException e) {}
+
+ // Post back to WM to handle clean-ups. We still need the input
+ // event handler for the last finishInputEvent()!
+ mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
+ }
+ handled = true;
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception caught by drag handleMotion", e);
+ } finally {
+ finishInputEvent(event, handled);
+ }
+ }
+ }
+
+ TaskPositioner(WindowManagerService service) {
+ mService = service;
+ }
+
+ /**
+ * @param display The Display that the window being dragged is on.
+ */
+ void register(Display display) {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "Registering task positioner");
+ }
+
+ if (mClientChannel != null) {
+ Slog.e(TAG, "Task positioner already registered");
+ return;
+ }
+
+ mDisplay = display;
+ mDisplay.getMetrics(mDisplayMetrics);
+ final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
+ mServerChannel = channels[0];
+ mClientChannel = channels[1];
+ mService.mInputManager.registerInputChannel(mServerChannel, null);
+
+ mInputEventReceiver = new WindowPositionerEventReceiver(
+ mClientChannel, mService.mH.getLooper(), mService.mChoreographer);
+
+ mDragApplicationHandle = new InputApplicationHandle(null);
+ mDragApplicationHandle.name = TAG;
+ mDragApplicationHandle.dispatchingTimeoutNanos =
+ WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
+ mDisplay.getDisplayId());
+ mDragWindowHandle.name = TAG;
+ mDragWindowHandle.inputChannel = mServerChannel;
+ mDragWindowHandle.layer = getDragLayerLocked();
+ mDragWindowHandle.layoutParamsFlags = 0;
+ mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
+ mDragWindowHandle.dispatchingTimeoutNanos =
+ WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ mDragWindowHandle.visible = true;
+ mDragWindowHandle.canReceiveKeys = false;
+ mDragWindowHandle.hasFocus = true;
+ mDragWindowHandle.hasWallpaper = false;
+ mDragWindowHandle.paused = false;
+ mDragWindowHandle.ownerPid = Process.myPid();
+ mDragWindowHandle.ownerUid = Process.myUid();
+ mDragWindowHandle.inputFeatures = 0;
+ mDragWindowHandle.scaleFactor = 1.0f;
+
+ // The drag window cannot receive new touches.
+ mDragWindowHandle.touchableRegion.setEmpty();
+
+ // The drag window covers the entire display
+ mDragWindowHandle.frameLeft = 0;
+ mDragWindowHandle.frameTop = 0;
+ final Point p = new Point();
+ mDisplay.getRealSize(p);
+ mDragWindowHandle.frameRight = p.x;
+ mDragWindowHandle.frameBottom = p.y;
+
+ // Pause rotations before a drag.
+ if (WindowManagerService.DEBUG_ORIENTATION) {
+ Slog.d(TAG, "Pausing rotation during re-position");
+ }
+ mService.pauseRotationLocked();
+
+ mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
+ mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
+ mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
+ mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
+
+ mDragEnded = false;
+ }
+
+ void unregister() {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "Unregistering task positioner");
+ }
+
+ if (mClientChannel == null) {
+ Slog.e(TAG, "Task positioner not registered");
+ return;
+ }
+
+ mService.mInputManager.unregisterInputChannel(mServerChannel);
+
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
+ mClientChannel.dispose();
+ mServerChannel.dispose();
+ mClientChannel = null;
+ mServerChannel = null;
+
+ mDragWindowHandle = null;
+ mDragApplicationHandle = null;
+ mDisplay = null;
+
+ if (mDimLayer != null) {
+ mDimLayer.destroySurface();
+ mDimLayer = null;
+ }
+ mCurrentDimSide = CTRL_NONE;
+ mDragEnded = true;
+
+ // Resume rotations after a drag.
+ if (WindowManagerService.DEBUG_ORIENTATION) {
+ Slog.d(TAG, "Resuming rotation after re-position");
+ }
+ mService.resumeRotationLocked();
+ }
+
+ void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
+ + ", {" + startX + ", " + startY + "}");
+ }
+ mCtrlType = CTRL_NONE;
+ if (resize) {
+ final Rect visibleFrame = win.mVisibleFrame;
+ if (startX < visibleFrame.left) {
+ mCtrlType |= CTRL_LEFT;
+ }
+ if (startX > visibleFrame.right) {
+ mCtrlType |= CTRL_RIGHT;
+ }
+ if (startY < visibleFrame.top) {
+ mCtrlType |= CTRL_TOP;
+ }
+ if (startY > visibleFrame.bottom) {
+ mCtrlType |= CTRL_BOTTOM;
+ }
+ mResizing = true;
+ }
+
+ mTask = win.getTask();
+ mStartDragX = startX;
+ mStartDragY = startY;
+
+ mService.getTaskBounds(mTask.mTaskId, mWindowOriginalBounds);
+ }
+
+ private void endDragLocked() {
+ mResizing = false;
+ mTask.setDragResizing(false);
+ }
+
+ /** Returns true if the move operation should be ended. */
+ private boolean notifyMoveLocked(float x, float y) {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
+ }
+
+ if (mCtrlType != CTRL_NONE) {
+ // This is a resizing operation.
+ final int deltaX = Math.round(x - mStartDragX);
+ final int deltaY = Math.round(y - mStartDragY);
+ int left = mWindowOriginalBounds.left;
+ int top = mWindowOriginalBounds.top;
+ int right = mWindowOriginalBounds.right;
+ int bottom = mWindowOriginalBounds.bottom;
+ if ((mCtrlType & CTRL_LEFT) != 0) {
+ left = Math.min(left + deltaX, right - mMinVisibleWidth);
+ }
+ if ((mCtrlType & CTRL_TOP) != 0) {
+ top = Math.min(top + deltaY, bottom - mMinVisibleHeight);
+ }
+ if ((mCtrlType & CTRL_RIGHT) != 0) {
+ right = Math.max(left + mMinVisibleWidth, right + deltaX);
+ }
+ if ((mCtrlType & CTRL_BOTTOM) != 0) {
+ bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
+ }
+ mWindowDragBounds.set(left, top, right, bottom);
+ mTask.setDragResizing(true);
+ return false;
+ }
+
+ // This is a moving operation.
+ mTask.mStack.getBounds(mTmpRect);
+ mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
+ if (!mTmpRect.contains((int) x, (int) y)) {
+ // We end the moving operation if position is outside the stack bounds.
+ return true;
+ }
+ mWindowDragBounds.set(mWindowOriginalBounds);
+ mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
+ updateDimLayerVisibility((int) x);
+ return false;
+ }
+
+ private void updateDimLayerVisibility(int x) {
+ @CtrlType
+ int dimSide = getDimSide(x);
+ if (dimSide == mCurrentDimSide) {
+ return;
+ }
+
+ mCurrentDimSide = dimSide;
+
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
+ SurfaceControl.openTransaction();
+ if (mCurrentDimSide == CTRL_NONE) {
+ mDimLayer.hide();
+ } else {
+ showDimLayer();
+ }
+ SurfaceControl.closeTransaction();
+ }
+
+ /**
+ * Returns the side of the screen the dim layer should be shown.
+ * @param x horizontal coordinate used to determine if the dim layer should be shown
+ * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
+ * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
+ * shouldn't be shown.
+ */
+ private int getDimSide(int x) {
+ if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+ || !mTask.mStack.isFullscreen()
+ || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
+ return CTRL_NONE;
+ }
+
+ mTask.mStack.getBounds(mTmpRect);
+ if (x - mSideMargin <= mTmpRect.left) {
+ return CTRL_LEFT;
+ }
+ if (x + mSideMargin >= mTmpRect.right) {
+ return CTRL_RIGHT;
+ }
+
+ return CTRL_NONE;
+ }
+
+ private void showDimLayer() {
+ mTask.mStack.getBounds(mTmpRect);
+ if (mCurrentDimSide == CTRL_LEFT) {
+ mTmpRect.right = mTmpRect.centerX();
+ } else if (mCurrentDimSide == CTRL_RIGHT) {
+ mTmpRect.left = mTmpRect.centerX();
+ }
+
+ mDimLayer.setBounds(mTmpRect);
+ mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
+ }
+
+ @Override /** {@link DimLayer.DimLayerUser} */
+ public boolean isFullscreen() {
+ return false;
+ }
+
+ @Override /** {@link DimLayer.DimLayerUser} */
+ public DisplayInfo getDisplayInfo() {
+ return mTask.mStack.getDisplayInfo();
+ }
+
+ private int getDragLayerLocked() {
+ return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
+ * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ + WindowManagerService.TYPE_LAYER_OFFSET;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index c3a8486..8053fed 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -18,8 +18,10 @@
import static android.app.ActivityManager.*;
import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
import static com.android.server.wm.WindowManagerService.TAG;
+import android.annotation.IntDef;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Debug;
@@ -29,9 +31,12 @@
import android.util.SparseArray;
import android.view.DisplayInfo;
+import android.view.Surface;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
public class TaskStack implements DimLayer.DimLayerUser {
@@ -54,6 +59,7 @@
/** For comparison with DisplayContent bounds. */
private Rect mTmpRect = new Rect();
+ private Rect TmpRect2 = new Rect();
/** Content limits relative to the DisplayContent this sits in. */
private Rect mBounds = new Rect();
@@ -61,6 +67,9 @@
/** Whether mBounds is fullscreen */
private boolean mFullscreen = true;
+ // Device rotation as of the last time {@link #mBounds} was set.
+ int mRotation;
+
/** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
DimLayer mAnimationBackgroundSurface;
@@ -73,6 +82,21 @@
/** Detach this stack from its display when animation completes. */
boolean mDeferDetach;
+ static final int DOCKED_INVALID = -1;
+ static final int DOCKED_LEFT = 1;
+ static final int DOCKED_TOP = 2;
+ static final int DOCKED_RIGHT = 3;
+ static final int DOCKED_BOTTOM = 4;
+
+ @IntDef({
+ DOCKED_INVALID,
+ DOCKED_LEFT,
+ DOCKED_TOP,
+ DOCKED_RIGHT,
+ DOCKED_BOTTOM})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface DockSide {}
+
TaskStack(WindowManagerService service, int stackId) {
mService = service;
mStackId = stackId;
@@ -93,33 +117,30 @@
}
}
+ boolean allowTaskResize() {
+ return mStackId == FREEFORM_WORKSPACE_STACK_ID
+ || mStackId == DOCKED_STACK_ID;
+ }
+
/**
* Set the bounds of the stack and its containing tasks.
* @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
- * @param resizeTasks If true, the tasks within the stack will also be resized.
* @param configs Configuration for individual tasks, keyed by task id.
* @param taskBounds Bounds for individual tasks, keyed by task id.
* @return True if the stack bounds was changed.
* */
- boolean setBounds(Rect stackBounds, boolean resizeTasks, SparseArray<Configuration> configs,
- SparseArray<Rect> taskBounds) {
+ boolean setBounds(
+ Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) {
if (!setBounds(stackBounds)) {
return false;
}
- if (!resizeTasks) {
- return true;
- }
-
// Update bounds of containing tasks.
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = mTasks.get(taskNdx);
Configuration config = configs.get(task.mTaskId);
if (config != null) {
Rect bounds = taskBounds.get(task.mTaskId);
- if (bounds == null) {
- bounds = stackBounds;
- }
task.setBounds(bounds, config);
} else {
Slog.wtf(TAG, "No config for task: " + task + ", is there a mismatch with AM?");
@@ -130,8 +151,10 @@
private boolean setBounds(Rect bounds) {
boolean oldFullscreen = mFullscreen;
+ int rotation = Surface.ROTATION_0;
if (mDisplayContent != null) {
mDisplayContent.getLogicalDisplayRect(mTmpRect);
+ rotation = mDisplayContent.getDisplayInfo().rotation;
if (bounds == null) {
bounds = mTmpRect;
mFullscreen = true;
@@ -149,12 +172,13 @@
// Can't set to fullscreen if we don't have a display to get bounds from...
return false;
}
- if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) {
+ if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
return false;
}
mAnimationBackgroundSurface.setBounds(bounds);
mBounds.set(bounds);
+ mRotation = rotation;
return true;
}
@@ -164,14 +188,25 @@
void updateDisplayInfo(Rect bounds) {
if (mDisplayContent != null) {
- if (bounds != null) {
- setBounds(bounds);
- } else {
- setBounds(mFullscreen ? null : mBounds);
- }
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
}
+ if (bounds != null) {
+ setBounds(bounds);
+ } else if (mFullscreen) {
+ setBounds(null);
+ } else {
+ TmpRect2.set(mBounds);
+ mDisplayContent.rotateBounds(
+ mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2);
+ if (setBounds(TmpRect2)) {
+ // Post message to inform activity manager of the bounds change simulating
+ // a one-way call. We do this to prevent a deadlock between window manager
+ // lock and activity manager lock been held.
+ mService.mH.sendMessage(
+ mService.mH.obtainMessage(RESIZE_STACK, mStackId, -1, mBounds));
+ }
+ }
}
}
@@ -325,7 +360,8 @@
// the docked stack occupies a dedicated region on screen.
bounds = new Rect();
displayContent.getLogicalDisplayRect(mTmpRect);
- getInitialDockedStackBounds(mTmpRect, bounds, mStackId);
+ getInitialDockedStackBounds(mTmpRect, bounds, mStackId,
+ mDisplayContent.mDividerControllerLocked.getWidthAdjustment());
}
updateDisplayInfo(bounds);
@@ -344,25 +380,29 @@
* @param displayRect The bounds of the display the docked stack is on.
* @param outBounds Output bounds that should be used for the stack.
* @param stackId Id of stack we are calculating the bounds for.
+ * @param adjustment
*/
- private static void getInitialDockedStackBounds(
- Rect displayRect, Rect outBounds, int stackId) {
+ private static void getInitialDockedStackBounds(Rect displayRect, Rect outBounds, int stackId,
+ int adjustment) {
// Docked stack start off occupying half the screen space.
- // TODO(multi-window): Need to support the selecting which half of the screen the
- // docked stack uses for snapping windows to the edge of the screen.
+ final boolean dockedStack = stackId == DOCKED_STACK_ID;
final boolean splitHorizontally = displayRect.width() > displayRect.height();
+ final boolean topOrLeftCreateMode =
+ WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ final boolean placeTopOrLeft = (dockedStack && topOrLeftCreateMode)
+ || (!dockedStack && !topOrLeftCreateMode);
outBounds.set(displayRect);
- if (stackId == DOCKED_STACK_ID) {
+ if (placeTopOrLeft) {
if (splitHorizontally) {
- outBounds.right = displayRect.centerX();
+ outBounds.right = displayRect.centerX() - adjustment;
} else {
- outBounds.bottom = displayRect.centerY();
+ outBounds.bottom = displayRect.centerY() - adjustment;
}
} else {
if (splitHorizontally) {
- outBounds.left = displayRect.centerX();
+ outBounds.left = displayRect.centerX() + adjustment;
} else {
- outBounds.top = displayRect.centerY();
+ outBounds.top = displayRect.centerY() + adjustment;
}
}
}
@@ -375,7 +415,8 @@
private void resizeNonDockedStacks(boolean fullscreen) {
mDisplayContent.getLogicalDisplayRect(mTmpRect);
if (!fullscreen) {
- getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID);
+ getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID,
+ mDisplayContent.mDividerControllerLocked.getWidth());
}
final int count = mService.mStackIdToStack.size();
@@ -405,8 +446,7 @@
for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
// We are in the middle of changing the state of displays/stacks/tasks. We need
// to finish that, before we let layout interfere with it.
- mService.removeWindowInnerLocked(appWindows.get(winNdx),
- false /* performLayout */);
+ mService.removeWindowLocked(appWindows.get(winNdx));
doAnotherLayoutPass = true;
}
}
@@ -501,4 +541,48 @@
public String toString() {
return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
}
+
+ /**
+ * For docked workspace provides information which side of the screen was the dock anchored.
+ */
+ @DockSide
+ int getDockSide() {
+ if (mStackId != DOCKED_STACK_ID) {
+ return DOCKED_INVALID;
+ }
+ if (mDisplayContent == null) {
+ return DOCKED_INVALID;
+ }
+ mDisplayContent.getLogicalDisplayRect(mTmpRect);
+ final int orientation = mService.mCurConfiguration.orientation;
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ // Portrait mode, docked either at the top or the bottom.
+ if (mBounds.top - mTmpRect.top < mTmpRect.bottom - mBounds.bottom) {
+ return DOCKED_TOP;
+ } else {
+ return DOCKED_BOTTOM;
+ }
+ } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ // Landscape mode, docked either on the left or on the right.
+ if (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) {
+ return DOCKED_LEFT;
+ } else {
+ return DOCKED_RIGHT;
+ }
+ } else {
+ return DOCKED_INVALID;
+ }
+ }
+
+ boolean isVisibleLocked() {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ Task task = mTasks.get(i);
+ for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
+ if (!task.mAppTokens.get(j).hidden) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index c97d12f..ce1b785 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -47,12 +47,23 @@
public void onPointerEvent(MotionEvent motionEvent) {
final int action = motionEvent.getAction();
switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_DOWN: {
mPointerId = motionEvent.getPointerId(0);
mDownX = motionEvent.getX();
mDownY = motionEvent.getY();
+
+ final int x = (int) mDownX;
+ final int y = (int) mDownY;
+ synchronized (this) {
+ if (!mTouchExcludeRegion.contains(x, y)) {
+ mService.mH.obtainMessage(H.TAP_DOWN_OUTSIDE_TASK, x, y,
+ mDisplayContent).sendToTarget();
+ }
+ }
break;
- case MotionEvent.ACTION_MOVE:
+ }
+
+ case MotionEvent.ACTION_MOVE: {
if (mPointerId >= 0) {
int index = motionEvent.findPointerIndex(mPointerId);
if ((motionEvent.getEventTime() - motionEvent.getDownTime()) > TAP_TIMEOUT_MSEC
@@ -63,6 +74,8 @@
}
}
break;
+ }
+
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP: {
int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index fc23fd1..1a946b2 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -736,7 +736,8 @@
insertionIndex = windows.indexOf(wallpaperTarget);
}
}
- if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT
+ || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
"Moving wallpaper " + wallpaper
+ " from " + oldIndex + " to " + insertionIndex);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 84e0b65..926bc0f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,37 @@
package com.android.server.wm;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+
+import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
+
import android.Manifest;
import android.animation.ValueAnimator;
import android.app.ActivityManagerNative;
@@ -79,6 +110,7 @@
import android.view.Choreographer;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.DropPermissionHolder;
import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.IInputFilter;
@@ -110,9 +142,7 @@
import android.view.WindowManagerPolicy.PointerEventListener;
import android.view.animation.Animation;
-import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
import com.android.internal.app.IAssistScreenshotReceiver;
-import com.android.internal.app.IBatteryStats;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -149,31 +179,6 @@
import java.util.Iterator;
import java.util.List;
-import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
@@ -207,6 +212,7 @@
static final boolean DEBUG_SURFACE_TRACE = false;
static final boolean DEBUG_WINDOW_TRACE = false;
static final boolean DEBUG_TASK_MOVEMENT = false;
+ static final boolean DEBUG_TASK_POSITIONING = false;
static final boolean DEBUG_STACK = false;
static final boolean DEBUG_DISPLAY = false;
static final boolean DEBUG_POWER = false;
@@ -457,6 +463,8 @@
private boolean mKeyguardWaitingForActivityDrawn;
+ static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+
class RotationWatcher {
IRotationWatcher watcher;
IBinder.DeathRecipient deathRecipient;
@@ -598,6 +606,7 @@
// Whether or not a layout can cause a wake up when theater mode is enabled.
boolean mAllowTheaterModeWakeFromLayout;
+ TaskPositioner mTaskPositioner;
DragState mDragState = null;
// For frozen screen animations.
@@ -621,6 +630,13 @@
private WindowContentFrameStats mTempWindowRenderStats;
+ private static final int DRAG_FLAGS_URI_ACCESS = View.DRAG_FLAG_GLOBAL_URI_READ |
+ View.DRAG_FLAG_GLOBAL_URI_WRITE;
+
+ private static final int DRAG_FLAGS_URI_PERMISSIONS = DRAG_FLAGS_URI_ACCESS |
+ View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION |
+ View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION;
+
final class DragInputEventReceiver extends InputEventReceiver {
// Set, if stylus button was down at the start of the drag.
private boolean mStylusButtonDownAtStart;
@@ -666,7 +682,7 @@
if (DEBUG_DRAG) Slog.d(TAG, "Button no longer pressed; dropping at "
+ newX + "," + newY);
synchronized (mWindowMap) {
- endDrag = mDragState.notifyDropLw(newX, newY);
+ endDrag = completeDrop(newX, newY);
}
} else {
synchronized (mWindowMap) {
@@ -680,7 +696,7 @@
if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at "
+ newX + "," + newY);
synchronized (mWindowMap) {
- endDrag = mDragState.notifyDropLw(newX, newY);
+ endDrag = completeDrop(newX, newY);
}
} break;
@@ -710,6 +726,25 @@
}
}
+ private boolean completeDrop(float x, float y) {
+ WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y);
+
+ DropPermissionHolder dropPermissionHolder = null;
+ if ((mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
+ (mDragState.mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
+ dropPermissionHolder = new DropPermissionHolder(
+ mDragState.mData,
+ mActivityManager,
+ mDragState.mUid,
+ dropTargetWin.getOwningPackage(),
+ mDragState.mFlags & DRAG_FLAGS_URI_PERMISSIONS,
+ UserHandle.getUserId(mDragState.mUid),
+ UserHandle.getUserId(dropTargetWin.getOwningUid()));
+ }
+
+ return mDragState.notifyDropLw(dropTargetWin, dropPermissionHolder, x, y);
+ }
+
/**
* Whether the UI is currently running in touch mode (not showing
* navigational focus because the user is directly pressing the screen).
@@ -889,6 +924,7 @@
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
+
LocalServices.addService(WindowManagerInternal.class, new LocalService());
initPolicy();
@@ -902,7 +938,6 @@
SurfaceControl.closeTransaction();
}
- updateCircularDisplayMaskIfNeeded();
showEmulatorDisplayOverlayIfNeeded();
}
@@ -1823,6 +1858,11 @@
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
+ } else if (type == TYPE_DOCK_DIVIDER) {
+ if (displayContent.mDividerControllerLocked.hasDivider()) {
+ Slog.w(TAG, "Attempted to add docked stack divider twice. Aborting.");
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
} else if (token.appWindowToken != null) {
Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type);
// It is not valid to use an app token with other system types; we will
@@ -1855,13 +1895,13 @@
return res;
}
- if (outInputChannel != null && (attrs.inputFeatures
- & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
+ final boolean openInputChannels = (outInputChannel != null
+ && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ if (openInputChannels) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
-
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
@@ -2135,10 +2175,7 @@
// The exit animation is running... wait for it!
win.mExiting = true;
win.mRemoveOnExit = true;
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
final boolean focusChanged = updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
mWindowPlacerLocked.performSurfacePlacement();
@@ -2167,7 +2204,7 @@
removeWindowInnerLocked(win, true);
}
- void removeWindowInnerLocked(WindowState win, boolean performLayout) {
+ private void removeWindowInnerLocked(WindowState win, boolean performLayout) {
if (win.mRemoved) {
// Nothing to do.
return;
@@ -2261,10 +2298,7 @@
windows.remove(win);
if (!mWindowPlacerLocked.isInLayout()) {
assignLayersLocked(windows);
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
if (performLayout) {
mWindowPlacerLocked.performSurfacePlacement();
}
@@ -2346,10 +2380,7 @@
w.mGivenVisibleInsets.scale(w.mGlobalScale);
w.mGivenTouchableRegion.scale(w.mGlobalScale);
}
- final DisplayContent displayContent = w.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ w.setDisplayLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -2408,6 +2439,7 @@
boolean inTouchMode;
boolean configChanged;
boolean surfaceChanged = false;
+ boolean dragResizing = false;
boolean animating;
boolean hasStatusBarPermission =
mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
@@ -2556,6 +2588,23 @@
surfaceChanged = true;
}
}
+
+ // If we're starting a drag-resize, we'll be changing the surface size as well as
+ // notifying the client to render to with an offset from the surface's top-left.
+ // Do a screen freeze, and keep the old surface until the the first frame drawn to
+ // the new surface comes back, so that we avoid a flash due to mismatching surface
+ // setups on window manager side and client side.
+ if (win.isDragResizeChanged()) {
+ win.setDragResizing();
+ if (win.mHasSurface) {
+ winAnimator.mDestroyPendingSurfaceUponRedraw = true;
+ winAnimator.mSurfaceDestroyDeferred = true;
+ winAnimator.destroySurfaceLocked();
+ startFreezingDisplayLocked(false, 0, 0);
+ toBeDisplayed = true;
+ }
+ }
+ dragResizing = win.isDragResizing();
try {
if (!win.mHasSurface) {
surfaceChanged = true;
@@ -2673,10 +2722,7 @@
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
configChanged = updateOrientationFromAppTokensLocked(false);
mWindowPlacerLocked.performSurfacePlacement();
@@ -2722,7 +2768,8 @@
return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0)
| (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0)
- | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0);
+ | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0)
+ | (dragResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING : 0);
}
public void performDeferredDestroyWindow(Session session, IWindow client) {
@@ -2768,10 +2815,7 @@
getDefaultDisplayContentLocked().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
mWindowPlacerLocked.requestTraversal();
}
}
@@ -3054,7 +3098,7 @@
public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId,
int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
- Rect taskBounds, Configuration config) {
+ Rect taskBounds, Configuration config, boolean cropWindowsToStack) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3088,6 +3132,7 @@
atoken.layoutConfigChanges = (configChanges &
(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
atoken.mLaunchTaskBehind = launchTaskBehind;
+ atoken.mCropWindowsToStack = cropWindowsToStack;
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
+ " to stack=" + stackId + " task=" + taskId + " at " + addPos);
@@ -3209,8 +3254,7 @@
// if we're about to tear down this window and not seek for
// the behind activity, don't use it for orientation
- if (!findingBehind
- && (!atoken.hidden && atoken.hiddenRequested)) {
+ if (!findingBehind && !atoken.hidden && atoken.hiddenRequested) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
+ " -- going to hide");
continue;
@@ -3231,7 +3275,7 @@
}
// We ignore any hidden applications on the top.
- if (atoken.hiddenRequested || atoken.willBeHidden) {
+ if (atoken.hiddenRequested) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
+ " -- hidden on top");
continue;
@@ -3624,12 +3668,8 @@
// pretend like we didn't see that.
return;
}
- final boolean windowIsTranslucentDefined = ent.array.hasValue(
- com.android.internal.R.styleable.Window_windowIsTranslucent);
final boolean windowIsTranslucent = ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsTranslucent, false);
- final boolean windowSwipeToDismiss = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowSwipeToDismiss, false);
final boolean windowIsFloating = ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsFloating, false);
final boolean windowShowWallpaper = ent.array.getBoolean(
@@ -3639,7 +3679,7 @@
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Translucent=" + windowIsTranslucent
+ " Floating=" + windowIsFloating
+ " ShowWallpaper=" + windowShowWallpaper);
- if (windowIsTranslucent || (!windowIsTranslucentDefined && windowSwipeToDismiss)) {
+ if (windowIsTranslucent) {
return;
}
if (windowIsFloating || windowDisableStarting) {
@@ -3731,7 +3771,6 @@
if (!ttoken.hidden) {
wtoken.hidden = false;
wtoken.hiddenRequested = false;
- wtoken.willBeHidden = false;
}
if (wtoken.clientHidden != ttoken.clientHidden) {
wtoken.clientHidden = ttoken.clientHidden;
@@ -3787,25 +3826,6 @@
}
}
- @Override
- public void setAppWillBeHidden(IBinder token) {
- if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
- "setAppWillBeHidden()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
-
- AppWindowToken wtoken;
-
- synchronized(mWindowMap) {
- wtoken = findAppWindowToken(token);
- if (wtoken == null) {
- Slog.w(TAG, "Attempted to set will be hidden of non-existing app token: " + token);
- return;
- }
- wtoken.willBeHidden = true;
- }
- }
-
public void setAppFullscreen(IBinder token, boolean toOpaque) {
synchronized (mWindowMap) {
AppWindowToken atoken = findAppWindowToken(token);
@@ -3842,7 +3862,6 @@
wtoken.sendAppVisibilityToClients();
}
- wtoken.willBeHidden = false;
// Allow for state changes and animation to be applied if:
// * token is transitioning visibility state
// * or the token was marked as hidden and is exiting before we had a chance to play the
@@ -3895,10 +3914,7 @@
}
}
changed = true;
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
}
} else if (win.isVisibleNow()) {
if (!runningAppAnimation) {
@@ -3912,10 +3928,7 @@
}
}
changed = true;
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ win.setDisplayLayoutNeeded();
}
}
@@ -4083,10 +4096,7 @@
}
w.mLastFreezeDuration = 0;
unfrozeWindows = true;
- final DisplayContent displayContent = w.getDisplayContent();
- if (displayContent != null) {
- displayContent.layoutNeeded = true;
- }
+ w.setDisplayLayoutNeeded();
}
}
if (force || unfrozeWindows) {
@@ -4446,6 +4456,12 @@
}
}
+ public void setDockedStackCreateMode(int mode) {
+ synchronized (mWindowMap) {
+ sDockedStackCreateMode = mode;
+ }
+ }
+
/**
* Create a new TaskStack and place it on a DisplayContent.
* @param stackId The unique identifier of the new stack.
@@ -4468,7 +4484,10 @@
}
stack.attachDisplayContent(displayContent);
displayContent.attachStack(stack, onTop);
-
+ if (stack.mStackId == DOCKED_STACK_ID) {
+ mH.obtainMessage(H.UPDATE_DOCKED_STACK_DIVIDER,
+ displayContent).sendToTarget();
+ }
moveStackWindowsLocked(displayContent);
final WindowList windows = displayContent.getWindowList();
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
@@ -4491,6 +4510,11 @@
void detachStackLocked(DisplayContent displayContent, TaskStack stack) {
displayContent.detachStack(stack);
stack.detachDisplay();
+ // We can't directly remove the divider, because only the WM thread can do these operations
+ // and we can be on AM thread.
+ if (stack.mStackId == DOCKED_STACK_ID) {
+ mH.obtainMessage(H.UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget();
+ }
}
public void detachStack(int stackId) {
@@ -4579,12 +4603,11 @@
* Re-sizes a stack and its containing tasks.
* @param stackId Id of stack to resize.
* @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
- * @param resizeTasks If true, the tasks within the stack will also be resized.
* @param configs Configurations for tasks in the resized stack, keyed by task id.
* @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
* @return True if the stack is now fullscreen.
* */
- public boolean resizeStack(int stackId, Rect bounds, boolean resizeTasks,
+ public boolean resizeStack(int stackId, Rect bounds,
SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) {
synchronized (mWindowMap) {
final TaskStack stack = mStackIdToStack.get(stackId);
@@ -4592,7 +4615,7 @@
throw new IllegalArgumentException("resizeStack: stackId " + stackId
+ " not found.");
}
- if (stack.setBounds(bounds, resizeTasks, configs, taskBounds)) {
+ if (stack.setBounds(bounds, configs, taskBounds)) {
stack.resizeWindows();
stack.getDisplayContent().layoutNeeded = true;
mWindowPlacerLocked.performSurfacePlacement();
@@ -4629,19 +4652,18 @@
* Returns a {@link Configuration} object that contains configurations settings
* that should be overridden due to the operation.
*/
- public void resizeTask(int taskId, Rect bounds, Configuration configuration, boolean relayout) {
+ public void resizeTask(int taskId, Rect bounds, Configuration configuration,
+ boolean relayout, boolean forced) {
synchronized (mWindowMap) {
Task task = mTaskIdToTask.get(taskId);
if (task == null) {
throw new IllegalArgumentException("resizeTask: taskId " + taskId
+ " not found.");
}
- if (task.setBounds(bounds, configuration)) {
- task.resizeWindows();
- if (relayout) {
- task.getDisplayContent().layoutNeeded = true;
- mWindowPlacerLocked.performSurfacePlacement();
- }
+
+ if (task.resizeLocked(bounds, configuration, forced) && relayout) {
+ task.getDisplayContent().layoutNeeded = true;
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
}
@@ -5292,7 +5314,7 @@
}
}
- public void updateCircularDisplayMaskIfNeeded() {
+ private void updateCircularDisplayMaskIfNeeded() {
// we're fullscreen and not hosted in an ActivityView
if (mContext.getResources().getConfiguration().isScreenRound()
&& mContext.getResources().getBoolean(
@@ -5620,7 +5642,7 @@
int right = wf.right - cr.right;
int bottom = wf.bottom - cr.bottom;
frame.union(left, top, right, bottom);
- ws.getTaskBounds(stackBounds, !BOUNDS_FOR_TOUCH);
+ ws.getVisibleBounds(stackBounds, !BOUNDS_FOR_TOUCH);
if (!frame.intersect(stackBounds)) {
// Set frame empty if there's no intersection.
frame.setEmpty();
@@ -6817,6 +6839,80 @@
}
}
+ boolean startMovingTask(IWindow window, float startX, float startY) {
+ WindowState win = null;
+ synchronized (mWindowMap) {
+ win = windowForClientLocked(null, window, false);
+ if (!startPositioningLocked(win, false /*resize*/, startX, startY)) {
+ return false;
+ }
+ }
+ try {
+ mActivityManager.setFocusedTask(win.getTask().mTaskId);
+ } catch(RemoteException e) {}
+ return true;
+ }
+
+ private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
+ WindowState win = null;
+ synchronized (mWindowMap) {
+ win = displayContent.findWindowForControlPoint(startX, startY);
+ if (!startPositioningLocked(win, true /*resize*/, startX, startY)) {
+ return;
+ }
+ }
+ try {
+ mActivityManager.setFocusedTask(win.getTask().mTaskId);
+ } catch(RemoteException e) {}
+ }
+
+ private boolean startPositioningLocked(
+ WindowState win, boolean resize, float startX, float startY) {
+ if (WindowManagerService.DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "startPositioningLocked: win=" + win +
+ ", resize=" + resize + ", {" + startX + ", " + startY + "}");
+ }
+ if (win == null || win.getAppToken() == null || !win.inFreeformWorkspace()) {
+ Slog.w(TAG, "startPositioningLocked: Bad window " + win);
+ return false;
+ }
+
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent == null) {
+ Slog.w(TAG, "startPositioningLocked: Invalid display content " + win);
+ return false;
+ }
+
+ Display display = displayContent.getDisplay();
+ mTaskPositioner = new TaskPositioner(this);
+ mTaskPositioner.register(display);
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+ if (!mInputManager.transferTouchFocus(
+ win.mInputChannel, mTaskPositioner.mServerChannel)) {
+ Slog.e(TAG, "startPositioningLocked: Unable to transfer touch focus");
+ mTaskPositioner.unregister();
+ mTaskPositioner = null;
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+ return false;
+ }
+
+ mTaskPositioner.startDragLocked(win, resize, startX, startY);
+ return true;
+ }
+
+ private void finishPositioning() {
+ if (WindowManagerService.DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "finishPositioning");
+ }
+ synchronized (mWindowMap) {
+ if (mTaskPositioner != null) {
+ mTaskPositioner.unregister();
+ mTaskPositioner = null;
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+ }
+ }
+ }
+
// -------------------------------------------------------------
// Drag and drop
// -------------------------------------------------------------
@@ -7002,6 +7098,8 @@
mActivityManager.updateConfiguration(null);
} catch (RemoteException e) {
}
+
+ updateCircularDisplayMaskIfNeeded();
}
private void displayReady(int displayId) {
@@ -7082,6 +7180,14 @@
public static final int RESET_ANR_MESSAGE = 38;
public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39;
+ public static final int TAP_DOWN_OUTSIDE_TASK = 40;
+ public static final int FINISH_TASK_POSITIONING = 41;
+
+ public static final int UPDATE_DOCKED_STACK_DIVIDER = 42;
+
+ public static final int RESIZE_STACK = 43;
+ public static final int RESIZE_TASK = 44;
+
@Override
public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) {
@@ -7542,6 +7648,17 @@
}
}
break;
+
+ case TAP_DOWN_OUTSIDE_TASK: {
+ startResizingTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
+ }
+ break;
+
+ case FINISH_TASK_POSITIONING: {
+ finishPositioning();
+ }
+ break;
+
case NOTIFY_ACTIVITY_DRAWN:
try {
mActivityManager.notifyActivityDrawn((IBinder) msg.obj);
@@ -7610,6 +7727,29 @@
}
}
break;
+ case UPDATE_DOCKED_STACK_DIVIDER: {
+ DisplayContent content = (DisplayContent) msg.obj;
+ synchronized (mWindowMap) {
+ content.mDividerControllerLocked.update();
+ }
+ }
+ break;
+ case RESIZE_TASK: {
+ try {
+ mActivityManager.resizeTask(msg.arg1, (Rect) msg.obj, msg.arg2);
+ } catch (RemoteException e) {
+ // This will not happen since we are in the same process.
+ }
+ }
+ break;
+ case RESIZE_STACK: {
+ try {
+ mActivityManager.resizeStack(msg.arg1, (Rect) msg.obj);
+ } catch (RemoteException e) {
+ // This will not happen since we are in the same process.
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -8151,7 +8291,7 @@
final int numTokens = tokens.size();
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
- if (wtoken.mIsExiting) {
+ if (wtoken.mIsExiting && !wtoken.mReplacingWindow) {
continue;
}
i = reAddAppWindowsLocked(displayContent, i, wtoken);
@@ -8326,15 +8466,18 @@
Slog.v(TAG, "Win " + w + " config changed: "
+ mCurConfiguration);
}
+ final boolean dragResizingChanged = w.isDragResizeChanged();
if (localLOGV) Slog.v(TAG, "Resizing " + w
+ ": configChanged=" + configChanged
+ + " dragResizingChanged=" + dragResizingChanged
+ " last=" + w.mLastFrame + " frame=" + w.mFrame);
w.mLastFrame.set(w.mFrame);
if (w.mContentInsetsChanged
|| w.mVisibleInsetsChanged
|| winAnimator.mSurfaceResized
|| w.mOutsetsChanged
- || configChanged) {
+ || configChanged
+ || dragResizingChanged) {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
Slog.v(TAG, "Resize reasons for w=" + w + ": "
+ " contentInsetsChanged=" + w.mContentInsetsChanged
@@ -8346,7 +8489,8 @@
+ " outsetsChanged=" + w.mOutsetsChanged
+ " " + w.mOutsets.toShortString()
+ " surfaceResized=" + winAnimator.mSurfaceResized
- + " configChanged=" + configChanged);
+ + " configChanged=" + configChanged
+ + " dragResizingChanged=" + dragResizingChanged);
}
w.mLastOverscanInsets.set(w.mOverscanInsets);
@@ -8355,12 +8499,12 @@
w.mLastStableInsets.set(w.mStableInsets);
w.mLastOutsets.set(w.mOutsets);
makeWindowFreezingScreenIfNeededLocked(w);
- // If the orientation is changing, then we need to
- // hold off on unfreezing the display until this
- // window has been redrawn; to do that, we need
- // to go through the process of getting informed
- // by the application when it has finished drawing.
- if (w.mOrientationChanging) {
+ // If the orientation is changing, or we're starting or ending
+ // a drag resizing action, then we need to hold off on unfreezing
+ // the display until this window has been redrawn; to do that,
+ // we need to go through the process of getting informed by the
+ // application when it has finished drawing.
+ if (w.mOrientationChanging || dragResizingChanged) {
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation start waiting for draw mDrawState=DRAW_PENDING in "
+ w + ", surface " + winAnimator.mSurfaceControl);
@@ -9739,6 +9883,12 @@
return mWindowMap;
}
+ /**
+ * Hint to a token that its activity will relaunch, which will trigger removal and addition of
+ * a window.
+ * @param token Application token for which the activity will be relaunched.
+ * @param animate Whether to animate the addition of the new window.
+ */
public void setReplacingWindow(IBinder token, boolean animate) {
synchronized (mWindowMap) {
AppWindowToken appWindowToken = findAppWindowToken(token);
@@ -9751,6 +9901,10 @@
}
}
+ static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
+ return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
+ }
+
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
@@ -9924,8 +10078,6 @@
@Override
public void saveLastInputMethodWindowForTransition() {
synchronized (mWindowMap) {
- // TODO(multidisplay): Pass in the displayID.
- DisplayContent displayContent = getDefaultDisplayContentLocked();
if (mInputMethodWindow != null) {
mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 779f342..55ddbc0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -24,6 +24,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -34,10 +35,12 @@
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.WorkSource;
import android.util.DisplayMetrics;
import android.util.TimeUtils;
@@ -81,12 +84,14 @@
static final String TAG = "WindowState";
// The minimal size of a window within the usable area of the freeform stack.
- private static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48;
- private static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32;
+ // TODO(multi-window): fix the min sizes when we have mininum width/height support,
+ // use hard-coded min sizes for now.
+ static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48;
+ static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32;
// The thickness of a window resize handle outside the window bounds on the free form workspace
// to capture touch events in that area.
- private static final int RESIZE_HANDLE_WIDTH_IN_DP = 10;
+ static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
static final boolean BOUNDS_FOR_TOUCH = true;
@@ -126,6 +131,7 @@
boolean mAppFreezing;
boolean mAttachedHidden; // is our parent window hidden?
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
+ boolean mDragResizing;
RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -379,6 +385,8 @@
*/
PowerManager.WakeLock mDrawLock;
+ final private Rect mTmpRect = new Rect();
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, final DisplayContent displayContent) {
@@ -555,9 +563,9 @@
}
mHaveFrame = true;
- final Task task = mAppToken != null ? getTask() : null;
+ final Task task = getTask();
final boolean nonFullscreenTask = task != null && !task.isFullscreen();
- final boolean freeformWorkspace = inFreeformWorkspace();
+ final boolean freeformWorkspace = task != null && task.inFreeformWorkspace();
if (nonFullscreenTask) {
task.getBounds(mContainingFrame);
final WindowState imeWin = mService.mInputMethodWindow;
@@ -689,8 +697,11 @@
// into a usable area..
final int height = Math.min(mFrame.height(), mContentFrame.height());
final int width = Math.min(mContentFrame.width(), mFrame.width());
- final int minVisibleHeight = calculatePixelFromDp(MINIMUM_VISIBLE_HEIGHT_IN_DP);
- final int minVisibleWidth = calculatePixelFromDp(MINIMUM_VISIBLE_WIDTH_IN_DP);
+ final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+ final int minVisibleHeight =
+ mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
+ final int minVisibleWidth =
+ mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
final int top = Math.max(mContentFrame.top,
Math.min(mFrame.top, mContentFrame.bottom - minVisibleHeight));
final int left = Math.max(mContentFrame.left + minVisibleWidth - width,
@@ -699,6 +710,8 @@
mContentFrame.set(mFrame);
mVisibleFrame.set(mContentFrame);
mStableFrame.set(mContentFrame);
+ } else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
+ mDisplayContent.mDividerControllerLocked.positionDockedStackedDivider(mFrame);
} else {
mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
Math.max(mContentFrame.top, mFrame.top),
@@ -897,6 +910,11 @@
return stack == null ? mDisplayContent : stack.getDisplayContent();
}
+ public DisplayInfo getDisplayInfo() {
+ final DisplayContent displayContent = getDisplayContent();
+ return displayContent != null ? displayContent.getDisplayInfo() : null;
+ }
+
public int getDisplayId() {
final DisplayContent displayContent = getDisplayContent();
if (displayContent == null) {
@@ -906,12 +924,7 @@
}
Task getTask() {
- AppWindowToken wtoken = mAppToken == null ? mService.mFocusedApp : mAppToken;
- if (wtoken == null) {
- return null;
- }
- final Task task = wtoken.mTask;
- return task;
+ return mAppToken != null ? mAppToken.mTask : null;
}
TaskStack getStack() {
@@ -921,28 +934,45 @@
return task.mStack;
}
}
- return mDisplayContent.getHomeStack();
+ return null;
}
/**
- * Retrieves the bounds for a task.
+ * Retrieves the visible bounds of the window.
* @param bounds The rect which gets the bounds.
* @param forTouch Pass in BOUNDS_FOR_TOUCH to get touch related bounds, otherwise visible
* bounds will be returned.
*/
- void getTaskBounds(Rect bounds, boolean forTouch) {
- final Task task = getTask();
- if (task != null) {
- task.getBounds(bounds);
- if (forTouch == BOUNDS_FOR_TOUCH) {
- if (inFreeformWorkspace()) {
- final int delta = calculatePixelFromDp(RESIZE_HANDLE_WIDTH_IN_DP);
- bounds.inset(-delta, -delta);
- }
+ void getVisibleBounds(Rect bounds, boolean forTouch) {
+ boolean intersectWithStackBounds = mAppToken != null && mAppToken.mCropWindowsToStack;
+ bounds.setEmpty();
+ mTmpRect.setEmpty();
+ if (intersectWithStackBounds) {
+ final TaskStack stack = getStack();
+ if (stack != null) {
+ stack.getBounds(mTmpRect);
+ } else {
+ intersectWithStackBounds = false;
+ }
+ }
+
+ bounds.set(mVisibleFrame);
+ if (intersectWithStackBounds) {
+ bounds.intersect(mTmpRect);
+ }
+
+ if (bounds.isEmpty()) {
+ bounds.set(mFrame);
+ if (intersectWithStackBounds) {
+ bounds.intersect(mTmpRect);
}
return;
}
- bounds.set(mFrame);
+ if (forTouch && inFreeformWorkspace()) {
+ final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+ final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
+ bounds.inset(-delta, -delta);
+ }
}
public long getInputDispatchingTimeoutNanos() {
@@ -1293,6 +1323,12 @@
}
}
+ void setDisplayLayoutNeeded() {
+ if (mDisplayContent != null) {
+ mDisplayContent.layoutNeeded = true;
+ }
+ }
+
private class DeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
@@ -1437,10 +1473,7 @@
// We want the tag name to be somewhat stable so that it is easier to correlate
// in wake lock statistics. So in particular, we don't want to include the
// window's hash code as in toString().
- CharSequence tag = mAttrs.getTitle();
- if (tag == null) {
- tag = mAttrs.packageName;
- }
+ final CharSequence tag = getWindowTag();
mDrawLock = mService.mPowerManager.newWakeLock(
PowerManager.DRAW_WAKE_LOCK, "Window:" + tag);
mDrawLock.setReferenceCounted(false);
@@ -1577,6 +1610,7 @@
}
void reportResized() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mCompatFrame);
@@ -1642,6 +1676,7 @@
mService.mPendingRemove.add(this);
mService.mWindowPlacerLocked.requestTraversal();
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
public void registerFocusObserver(IWindowFocusObserver observer) {
@@ -1669,15 +1704,31 @@
boolean inFreeformWorkspace() {
final Task task = getTask();
- return task != null && task.mStack != null &&
- task.mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+ return task != null && task.inFreeformWorkspace();
}
- private int calculatePixelFromDp(int dp) {
- final Configuration serviceConfig = mService.mCurConfiguration;
- // TODO(multidisplay): Update Dp to that of display stack is on.
- final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
- return (int)(dp * density);
+ boolean isDragResizeChanged() {
+ return mDragResizing != computeDragResizing();
+ }
+
+ private boolean computeDragResizing() {
+ final Task task = getTask();
+ if (task == null) {
+ return false;
+ }
+ if (task.isDragResizing()) {
+ return true;
+ }
+ return mDisplayContent.mDividerControllerLocked.isResizing() &&
+ !task.inFreeformWorkspace() && !task.isFullscreen();
+ }
+
+ void setDragResizing() {
+ mDragResizing = computeDragResizing();
+ }
+
+ boolean isDragResizing() {
+ return mDragResizing;
}
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
@@ -1867,15 +1918,20 @@
String makeInputChannelName() {
return Integer.toHexString(System.identityHashCode(this))
- + " " + mAttrs.getTitle();
+ + " " + getWindowTag();
+ }
+
+ private CharSequence getWindowTag() {
+ CharSequence tag = mAttrs.getTitle();
+ if (tag == null || tag.length() <= 0) {
+ tag = mAttrs.packageName;
+ }
+ return tag;
}
@Override
public String toString() {
- CharSequence title = mAttrs.getTitle();
- if (title == null || title.length() <= 0) {
- title = mAttrs.packageName;
- }
+ final CharSequence title = getWindowTag();
if (mStringNameCache == null || mLastTitle != title || mWasExiting != mExiting) {
mLastTitle = title;
mWasExiting = mExiting;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index bf1ab8f..0b9f2c6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -107,6 +107,7 @@
*/
boolean mSurfaceDestroyDeferred;
+ boolean mDestroyPendingSurfaceUponRedraw;
float mShownAlpha = 0;
float mAlpha = 0;
float mLastAlpha = 0;
@@ -115,6 +116,7 @@
Rect mClipRect = new Rect();
Rect mTmpClipRect = new Rect();
Rect mLastClipRect = new Rect();
+ Rect mTmpStackBounds = new Rect();
// Used to save animation distances between the time they are calculated and when they are
// used.
@@ -168,6 +170,11 @@
/** Set when the window has been shown in the screen the first time. */
static final int HAS_DRAWN = 4;
+ // Surface flinger doesn't support crop rectangles where width or height is non-positive.
+ // However, we need to somehow handle the situation where the cropping would completely hide
+ // the window. We achieve this by explicitly hiding the surface and not letting it be shown.
+ private boolean mHiddenForCrop;
+
String drawStateToString() {
switch (mDrawState) {
case NO_SURFACE: return "NO_SURFACE";
@@ -540,9 +547,15 @@
Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceControl);
}
mDrawState = READY_TO_SHOW;
+ boolean result = false;
final AppWindowToken atoken = mWin.mAppToken;
if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
- return performShowLocked();
+ result = performShowLocked();
+ }
+ if (mDestroyPendingSurfaceUponRedraw) {
+ mDestroyPendingSurfaceUponRedraw = false;
+ destroyDeferredSurfaceLocked();
+ mService.stopFreezingDisplayLocked();
}
return false;
}
@@ -790,6 +803,9 @@
flags |= SurfaceControl.SECURE;
}
+ float left = w.mFrame.left + w.mXOffset;
+ float top = w.mFrame.top + w.mYOffset;
+
int width;
int height;
if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
@@ -798,8 +814,19 @@
width = w.mRequestedWidth;
height = w.mRequestedHeight;
} else {
- width = w.mCompatFrame.width();
- height = w.mCompatFrame.height();
+ // When we're doing a drag-resizing, request a surface that's fullscreen size,
+ // so that we don't need to reallocate during the process. This also prevents
+ // buffer drops due to size mismatch.
+ final DisplayInfo displayInfo = w.getDisplayInfo();
+ if (displayInfo != null && w.isDragResizing()) {
+ left = 0;
+ top = 0;
+ width = displayInfo.logicalWidth;
+ height = displayInfo.logicalHeight;
+ } else {
+ width = w.mCompatFrame.width();
+ height = w.mCompatFrame.height();
+ }
}
// Something is wrong and SurfaceFlinger will not like this,
@@ -811,9 +838,6 @@
height = 1;
}
- float left = w.mFrame.left + w.mXOffset;
- float top = w.mFrame.top + w.mYOffset;
-
// Adjust for surface insets.
width += attrs.surfaceInsets.left + attrs.surfaceInsets.right;
height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
@@ -1063,6 +1087,8 @@
mAnimator.getScreenRotationAnimationLocked(displayId);
final boolean screenAnimation =
screenRotationAnimation != null && screenRotationAnimation.isAnimating();
+
+ mHasClipRect = false;
if (selfTransformation || attachedTransformation != null
|| appTransformation != null || screenAnimation) {
// cache often used attributes locally
@@ -1141,7 +1167,6 @@
// transforming since it is more important to have that
// animation be smooth.
mShownAlpha = mAlpha;
- mHasClipRect = false;
if (!mService.mLimitedAlphaCompositing
|| (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
|| (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
@@ -1187,6 +1212,13 @@
return;
} else if (mIsWallpaper && mService.mWindowPlacerLocked.mWallpaperActionPending) {
return;
+ } else if (mWin.isDragResizeChanged()) {
+ // This window is awaiting a relayout because user just started (or ended)
+ // drag-resizing. The shown frame (which affects surface size and pos)
+ // should not be updated until we get next finished draw with the new surface.
+ // Otherwise one or two frames rendered with old settings would be displayed
+ // with new geometry.
+ return;
}
if (WindowManagerService.localLOGV) Slog.v(
@@ -1309,9 +1341,15 @@
final boolean fullscreen = w.isFullscreen(displayInfo.appWidth, displayInfo.appHeight);
final Rect clipRect = mTmpClipRect;
- // We use the clip rect as provided by the tranformation for non-fullscreen windows to
- // avoid premature clipping with the system decor rect.
- clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : w.mSystemDecorRect);
+ if (w.isDragResizing()) {
+ // When we're doing a drag-resizing, the surface is set up to cover full screen.
+ // Set the clip rect to be the same size so that we don't get any scaling.
+ clipRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ } else {
+ // We use the clip rect as provided by the tranformation for non-fullscreen windows to
+ // avoid premature clipping with the system decor rect.
+ clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : w.mSystemDecorRect);
+ }
// Expand the clip rect for surface insets.
final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -1331,12 +1369,25 @@
// so we need to translate to match the actual surface coordinates.
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
+ // We don't want to clip to stack bounds windows that are currently doing entrance
+ // animation. This is necessary for docking operation, otherwise the window will be
+ // suddenly cut off.
+ if (!mAnimator.mAnimating) {
+ adjustCropToStackBounds(w, clipRect);
+ }
+
if (!clipRect.equals(mLastClipRect)) {
mLastClipRect.set(clipRect);
try {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"CROP " + clipRect.toShortString(), null);
- mSurfaceControl.setWindowCrop(clipRect);
+ if (clipRect.width() > 0 && clipRect.height() > 0) {
+ mSurfaceControl.setWindowCrop(clipRect);
+ mHiddenForCrop = false;
+ } else {
+ hide();
+ mHiddenForCrop = true;
+ }
} catch (RuntimeException e) {
Slog.w(TAG, "Error setting crop surface of " + w
+ " crop=" + clipRect.toShortString(), e);
@@ -1347,9 +1398,32 @@
}
}
+ private void adjustCropToStackBounds(WindowState w, Rect clipRect) {
+ final AppWindowToken appToken = w.mAppToken;
+ if (appToken != null && appToken.mCropWindowsToStack) {
+ TaskStack stack = w.getTask().mStack;
+ stack.getBounds(mTmpStackBounds);
+ final int surfaceX = (int) mSurfaceX;
+ final int surfaceY = (int) mSurfaceY;
+ // We need to do some acrobatics with surface position, because their clip region is
+ // relative to the inside of the surface, but the stack bounds aren't.
+ clipRect.left = Math.max(0,
+ Math.max(mTmpStackBounds.left, surfaceX + clipRect.left) - surfaceX);
+ clipRect.top = Math.max(0,
+ Math.max(mTmpStackBounds.top, surfaceY + clipRect.top) - surfaceY);
+ clipRect.right = Math.max(0,
+ Math.min(mTmpStackBounds.right, surfaceX + clipRect.right) - surfaceX);
+ clipRect.bottom = Math.max(0,
+ Math.min(mTmpStackBounds.bottom, surfaceY + clipRect.bottom) - surfaceY);
+ }
+ }
+
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
+ float left = w.mShownFrame.left;
+ float top = w.mShownFrame.top;
+
int width;
int height;
if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
@@ -1358,8 +1432,19 @@
width = w.mRequestedWidth;
height = w.mRequestedHeight;
} else {
- width = w.mCompatFrame.width();
- height = w.mCompatFrame.height();
+ // When we're doing a drag-resizing, request a surface that's fullscreen size,
+ // so that we don't need to reallocate during the process. This also prevents
+ // buffer drops due to size mismatch.
+ final DisplayInfo displayInfo = w.getDisplayInfo();
+ if (displayInfo != null && w.isDragResizing()) {
+ left = 0;
+ top = 0;
+ width = displayInfo.logicalWidth;
+ height = displayInfo.logicalHeight;
+ } else {
+ width = w.mCompatFrame.width();
+ height = w.mCompatFrame.height();
+ }
}
// Something is wrong and SurfaceFlinger will not like this,
@@ -1371,9 +1456,6 @@
height = 1;
}
- float left = w.mShownFrame.left;
- float top = w.mShownFrame.top;
-
// Adjust for surface insets.
final LayoutParams attrs = w.getAttrs();
final int displayId = w.getDisplayId();
@@ -1517,7 +1599,7 @@
mDsDx * w.mHScale, mDtDx * w.mVScale,
mDsDy * w.mHScale, mDtDy * w.mVScale);
- if (mLastHidden && mDrawState == HAS_DRAWN) {
+ if (mLastHidden && mDrawState == HAS_DRAWN && !mHiddenForCrop) {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"SHOW (performLayout)", null);
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 52efa68..d86a8af 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -47,6 +47,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
@@ -625,8 +626,11 @@
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState w = windows.get(i);
- final Task task = w.getTask();
- if (task == null && w.getAttrs().type != TYPE_PRIVATE_PRESENTATION) {
+ Task task = w.getTask();
+ if (task == null && w.getDisplayContent().getHomeStack() == null
+ && w.getAttrs().type != TYPE_PRIVATE_PRESENTATION) {
+ // TODO: Understand what the use case is here and see if the conditions can be
+ // simplified.
continue;
}
@@ -712,7 +716,12 @@
}
}
}
-
+ /*
+ * Updates the shown frame before we set up the surface. This is needed because
+ * the resizing could change the top-left position (in addition to size) of the
+ * window. setSurfaceBoundariesLocked uses mShownFrame to position the surface.
+ */
+ winAnimator.computeShownFrameLocked();
winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
}
@@ -1220,11 +1229,15 @@
final WindowState oldWallpaper =
mWallpaperControllerLocked.isWallpaperTargetAnimating()
? null : wallpaperTarget;
+ final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps;
+ final ArraySet<AppWindowToken> closingApps = mService.mClosingApps;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New wallpaper target=" + wallpaperTarget
+ ", oldWallpaper=" + oldWallpaper
+ ", lower target=" + lowerWallpaperTarget
- + ", upper target=" + upperWallpaperTarget);
+ + ", upper target=" + upperWallpaperTarget
+ + ", openingApps=" + openingApps
+ + ", closingApps=" + closingApps);
mService.mAnimateWallpaperWithTarget = false;
if (closingAppHasWallpaper && openingAppHasWallpaper) {
if (DEBUG_APP_TRANSITIONS)
@@ -1243,15 +1256,16 @@
}
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New transit: " + AppTransition.appTransitionToString(transit));
- } else if ((oldWallpaper != null) && !mService.mOpeningApps.isEmpty()
- && !mService.mOpeningApps.contains(oldWallpaper.mAppToken)) {
- // We are transitioning from an activity with
- // a wallpaper to one without.
+ } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
+ && !openingApps.contains(oldWallpaper.mAppToken)
+ && closingApps.contains(oldWallpaper.mAppToken)) {
+ // We are transitioning from an activity with a wallpaper to one without.
transit = AppTransition.TRANSIT_WALLPAPER_CLOSE;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New transit away from wallpaper: "
+ AppTransition.appTransitionToString(transit));
- } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()) {
+ } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
+ openingApps.contains(wallpaperTarget.mAppToken)) {
// We are transitioning from an activity without
// a wallpaper to now showing the wallpaper
transit = AppTransition.TRANSIT_WALLPAPER_OPEN;
diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp
index 3fd0f84..5cbb277 100644
--- a/services/core/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/core/jni/com_android_server_AlarmManagerService.cpp
@@ -460,7 +460,7 @@
return result;
}
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"init", "()J", (void*)android_server_AlarmManagerService_init},
{"close", "(J)V", (void*)android_server_AlarmManagerService_close},
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 8f4fb51..ed79ceb 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -204,7 +204,7 @@
const char* const kClassPathName = "com/android/server/AssetAtlasService";
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{ "nUploadAtlas", "(Landroid/view/GraphicBuffer;Landroid/graphics/Bitmap;)Z",
(void*) com_android_server_AssetAtlasService_upload },
};
diff --git a/services/core/jni/com_android_server_ConsumerIrService.cpp b/services/core/jni/com_android_server_ConsumerIrService.cpp
index f5121cd..7104870 100644
--- a/services/core/jni/com_android_server_ConsumerIrService.cpp
+++ b/services/core/jni/com_android_server_ConsumerIrService.cpp
@@ -100,7 +100,7 @@
return freqsOut.getJavaArray();
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{ "halOpen", "()J", (void *)halOpen },
{ "halTransmit", "(JI[I)I", (void *)halTransmit },
{ "halGetCarrierFrequencies", "(J)[I", (void *)halGetCarrierFrequencies},
diff --git a/services/core/jni/com_android_server_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
index 4ccfa56..06de592 100644
--- a/services/core/jni/com_android_server_PersistentDataBlockService.cpp
+++ b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
@@ -97,7 +97,7 @@
return wipe_block_device(fd);
}
- static JNINativeMethod sMethods[] = {
+ static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"nativeGetBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_PersistentDataBlockService_getBlockDeviceSize},
{"nativeWipe", "(Ljava/lang/String;)I", (void*)com_android_server_PersistentDataBlockService_wipe},
diff --git a/services/core/jni/com_android_server_SerialService.cpp b/services/core/jni/com_android_server_SerialService.cpp
index d48d159..1bd7a59 100644
--- a/services/core/jni/com_android_server_SerialService.cpp
+++ b/services/core/jni/com_android_server_SerialService.cpp
@@ -55,7 +55,7 @@
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{ "native_open", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
(void*)android_server_SerialService_open },
};
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 64514a9..c7d6b95 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -37,7 +37,7 @@
/*
* JNI registration.
*/
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "startSensorService", "()V", (void*) android_server_SystemServer_startSensorService },
};
diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp
index a1bff9d..3733a55 100644
--- a/services/core/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp
@@ -118,7 +118,7 @@
return result;
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{ "nativeGetAccessoryStrings", "()[Ljava/lang/String;",
(void*)android_server_UsbDeviceManager_getAccessoryStrings },
{ "nativeOpenAccessory", "()Landroid/os/ParcelFileDescriptor;",
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index d8c172f..795f6aa 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -186,7 +186,7 @@
gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{ "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus },
{ "nativeOpenDevice", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
(void*)android_server_UsbHostManager_openDevice },
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index fb1166b..64278ed 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -46,7 +46,7 @@
vibrator_off();
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{ "vibratorExists", "()Z", (void*)vibratorExists },
{ "vibratorOn", "(J)V", (void*)vibratorOn },
{ "vibratorOff", "()V", (void*)vibratorOff }
diff --git a/services/core/jni/com_android_server_am_ActivityManagerService.cpp b/services/core/jni/com_android_server_am_ActivityManagerService.cpp
index 52217b9..50e4502 100644
--- a/services/core/jni/com_android_server_am_ActivityManagerService.cpp
+++ b/services/core/jni/com_android_server_am_ActivityManagerService.cpp
@@ -110,7 +110,6 @@
return 0;
}
char buf[17];
- char *curBuf = buf;
while (fgets(buf, 16, boost_cpuset_file)) {
//ALOGE("Appending FD %s to fg", buf);
int i = 0;
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index dfc5ef6..5c43659 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -170,7 +170,7 @@
return mergedreasonpos - mergedreason;
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{ "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
};
diff --git a/services/core/jni/com_android_server_connectivity_Vpn.cpp b/services/core/jni/com_android_server_connectivity_Vpn.cpp
index 7faeb49..2d0dfd2 100644
--- a/services/core/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/core/jni/com_android_server_connectivity_Vpn.cpp
@@ -350,7 +350,7 @@
//------------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
{"jniCreate", "(I)I", (void *)create},
{"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
{"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index f2d0f06..b72cf4d 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -384,7 +384,7 @@
return controller->isConnected(port) ? JNI_TRUE : JNI_FALSE ;
}
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
"(Lcom/android/server/hdmi/HdmiCecController;Landroid/os/MessageQueue;)J",
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
index 11388d8..bdc109d 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
+++ b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
@@ -120,7 +120,7 @@
}
-static JNINativeMethod gInputApplicationHandleMethods[] = {
+static const JNINativeMethod gInputApplicationHandleMethods[] = {
/* name, signature, funcPtr */
{ "nativeDispose", "()V",
(void*) android_server_InputApplicationHandle_nativeDispose },
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e29d0a9..1d4f047 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1369,7 +1369,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gInputManagerMethods[] = {
+static const JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
"(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J",
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.cpp b/services/core/jni/com_android_server_input_InputWindowHandle.cpp
index 01c51cf..92ef7f1 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.cpp
+++ b/services/core/jni/com_android_server_input_InputWindowHandle.cpp
@@ -210,7 +210,7 @@
}
-static JNINativeMethod gInputWindowHandleMethods[] = {
+static const JNINativeMethod gInputWindowHandleMethods[] = {
/* name, signature, funcPtr */
{ "nativeDispose", "()V",
(void*) android_server_InputWindowHandle_nativeDispose },
diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp
index b2b2783..3f074f5 100644
--- a/services/core/jni/com_android_server_lights_LightsService.cpp
+++ b/services/core/jni/com_android_server_lights_LightsService.cpp
@@ -126,7 +126,7 @@
}
}
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
{ "init_native", "()J", (void*)init_native },
{ "finalize_native", "(J)V", (void*)finalize_native },
{ "setLight_native", "(JIIIIII)V", (void*)setLight_native },
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
index c0a0c9c..774577d 100644
--- a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -1023,7 +1023,7 @@
env->ReleaseIntArrayElements(geofenceIdsArray, geofenceIds, 0 /*mode*/);
}
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
//{"name", "signature", functionPointer }
{"nativeClassInit", "()V", reinterpret_cast<void*>(ClassInit)},
{"nativeInit", "()V", reinterpret_cast<void*>(Init)},
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 5c27b1f..b8d4196 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -1434,7 +1434,7 @@
env->ReleaseStringUTFChars(config_content, data);
}
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
{"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 1662755..2fdb8e2 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -166,7 +166,7 @@
// ----------------------------------------------------------------------------
-static JNINativeMethod gPowerManagerServiceMethods[] = {
+static const JNINativeMethod gPowerManagerServiceMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()V",
(void*) nativeInit },
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 507bc9c..89b2a47 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -662,7 +662,7 @@
delete tvInputHal;
}
-static JNINativeMethod gTvInputHalMethods[] = {
+static const JNINativeMethod gTvInputHalMethods[] = {
/* name, signature, funcPtr */
{ "nativeOpen", "(Landroid/os/MessageQueue;)J",
(void*) nativeOpen },
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6d7343d..2dd7cde 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -29,6 +29,7 @@
import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accounts.AccountManager;
+import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
@@ -54,6 +55,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -73,6 +75,7 @@
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
@@ -111,6 +114,7 @@
import android.view.inputmethod.InputMethodManager;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
@@ -266,17 +270,12 @@
| DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
final Context mContext;
+ final Injector mInjector;
+ final IPackageManager mIPackageManager;
final UserManager mUserManager;
- final PowerManager.WakeLock mWakeLock;
final LocalService mLocalService;
- final PowerManager mPowerManager;
- final PowerManagerInternal mPowerManagerInternal;
-
- IWindowManager mIWindowManager;
- NotificationManager mNotificationManager;
-
// Stores and loads state on device and profile owners.
private final Owners mOwners;
@@ -346,7 +345,7 @@
final SparseArray<DevicePolicyData> mUserData = new SparseArray<>();
- Handler mHandler = new Handler();
+ final Handler mHandler;
BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -992,7 +991,6 @@
boolean removed = false;
if (DBG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
DevicePolicyData policy = getUserData(userHandle);
- IPackageManager pm = AppGlobals.getPackageManager();
synchronized (this) {
for (int i = policy.mAdminList.size() - 1; i >= 0; i--) {
ActiveAdmin aa = policy.mAdminList.get(i);
@@ -1001,9 +999,9 @@
// then check if the package and receiver still exist.
final String adminPackage = aa.info.getPackageName();
if (packageName == null || packageName.equals(adminPackage)) {
- if (pm.getPackageInfo(adminPackage, 0, userHandle) == null
- || pm.getReceiverInfo(aa.info.getComponent(), 0, userHandle)
- == null) {
+ if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null
+ || mIPackageManager.getReceiverInfo(
+ aa.info.getComponent(), 0, userHandle) == null) {
removed = true;
policy.mAdminList.remove(i);
policy.mAdminMap.remove(aa.info.getComponent());
@@ -1024,7 +1022,7 @@
|| packageName.equals(policy.mDelegatedCertInstallerPackage))) {
try {
// Check if delegated cert installer package is removed.
- if (pm.getPackageInfo(
+ if (mIPackageManager.getPackageInfo(
policy.mDelegatedCertInstallerPackage, 0, userHandle) == null) {
policy.mDelegatedCertInstallerPackage = null;
saveSettingsLocked(policy.mUserHandle);
@@ -1037,18 +1035,145 @@
}
/**
+ * Unit test will subclass it to inject mocks.
+ */
+ @VisibleForTesting
+ static class Injector {
+
+ private final Context mContext;
+
+ Injector(Context context) {
+ mContext = context;
+ }
+
+ Owners newOwners() {
+ return new Owners(mContext);
+ }
+
+ UserManager getUserManager() {
+ return UserManager.get(mContext);
+ }
+
+ NotificationManager getNotificationManager() {
+ return mContext.getSystemService(NotificationManager.class);
+ }
+
+ PowerManagerInternal getPowerManagerInternal() {
+ return LocalServices.getService(PowerManagerInternal.class);
+ }
+
+ IWindowManager getIWindowManager() {
+ return IWindowManager.Stub
+ .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
+ }
+
+ IActivityManager getIActivityManager() {
+ return ActivityManagerNative.getDefault();
+ }
+
+ IPackageManager getIPackageManager() {
+ return AppGlobals.getPackageManager();
+ }
+
+ IBackupManager getIBackupManager() {
+ return IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ }
+
+ IAudioService getIAudioService() {
+ return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
+ }
+
+ LockPatternUtils newLockPatternUtils() {
+ return new LockPatternUtils(mContext);
+ }
+
+ Looper getMyLooper() {
+ return Looper.myLooper();
+ }
+
+ long binderClearCallingIdentity() {
+ return Binder.clearCallingIdentity();
+ }
+
+ void binderRestoreCallingIdentity(long token) {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ int binderGetCallingUid() {
+ return Binder.getCallingUid();
+ }
+
+ int binderGetCallingPid() {
+ return Binder.getCallingPid();
+ }
+
+ UserHandle binderGetCallingUserHandle() {
+ return Binder.getCallingUserHandle();
+ }
+
+ boolean binderIsCallingUidMyUid() {
+ return getCallingUid() == Process.myUid();
+ }
+
+ File environmentGetUserSystemDirectory(int userId) {
+ return Environment.getUserSystemDirectory(userId);
+ }
+
+ void powerManagerGoToSleep(long time, int reason, int flags) {
+ mContext.getSystemService(PowerManager.class).goToSleep(time, reason, flags);
+ }
+
+ boolean systemPropertiesGetBoolean(String key, boolean def) {
+ return SystemProperties.getBoolean(key, def);
+ }
+
+ long systemPropertiesGetLong(String key, long def) {
+ return SystemProperties.getLong(key, def);
+ }
+
+ String systemPropertiesGet(String key, String def) {
+ return SystemProperties.get(key, def);
+ }
+
+ String systemPropertiesGet(String key) {
+ return SystemProperties.get(key);
+ }
+
+ void systemPropertiesSet(String key, String value) {
+ SystemProperties.set(key, value);
+ }
+
+ boolean userManagerIsSplitSystemUser() {
+ return UserManager.isSplitSystemUser();
+ }
+
+ String getDevicePolicyFilePathForSystemUser() {
+ return "/data/system/";
+ }
+ }
+
+ /**
* Instantiates the service.
*/
public DevicePolicyManagerService(Context context) {
- mContext = context;
- mOwners = new Owners(mContext);
- mUserManager = UserManager.get(mContext);
- mHasFeature = context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_DEVICE_ADMIN);
- mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM");
+ this(new Injector(context));
+ }
+
+ @VisibleForTesting
+ DevicePolicyManagerService(Injector injector) {
+ mInjector = injector;
+ mContext = Preconditions.checkNotNull(injector.mContext);
+ mHandler = new Handler(Preconditions.checkNotNull(injector.getMyLooper()));
+ mOwners = Preconditions.checkNotNull(injector.newOwners());
+
+ mUserManager = Preconditions.checkNotNull(injector.getUserManager());
+ mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
+
mLocalService = new LocalService();
+
+ mHasFeature = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
if (!mHasFeature) {
// Skip the rest of the initialization
return;
@@ -1060,17 +1185,17 @@
filter.addAction(Intent.ACTION_USER_STARTED);
filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addDataScheme("package");
- context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
LocalServices.addService(DevicePolicyManagerInternal.class, mLocalService);
}
@@ -1080,6 +1205,7 @@
* @param userHandle the user for whom to load the policy data
* @return
*/
+ @NonNull
DevicePolicyData getUserData(int userHandle) {
synchronized (this) {
DevicePolicyData policy = mUserData.get(userHandle);
@@ -1103,17 +1229,17 @@
* @return
*/
DevicePolicyData getUserDataUnchecked(int userHandle) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
return getUserData(userHandle);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
void removeUserData(int userHandle) {
synchronized (this) {
- if (userHandle == UserHandle.USER_OWNER) {
+ if (userHandle == UserHandle.USER_SYSTEM) {
Slog.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
return;
}
@@ -1124,7 +1250,7 @@
if (policy != null) {
mUserData.remove(userHandle);
}
- File policyFile = new File(Environment.getUserSystemDirectory(userHandle),
+ File policyFile = new File(mInjector.environmentGetUserSystemDirectory(userHandle),
DEVICE_POLICIES_XML);
policyFile.delete();
Slog.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
@@ -1132,7 +1258,7 @@
updateScreenCaptureDisabledInWindowManager(userHandle, false /* default value */);
}
- void loadDeviceOwner() {
+ void loadOwners() {
synchronized (this) {
mOwners.load();
updateDeviceOwnerLocked();
@@ -1164,7 +1290,7 @@
alarmTime = now + alarmInterval;
}
- long token = Binder.clearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD,
@@ -1176,26 +1302,10 @@
am.set(AlarmManager.RTC, alarmTime, pi);
}
} finally {
- Binder.restoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
}
- private IWindowManager getWindowManager() {
- if (mIWindowManager == null) {
- IBinder b = ServiceManager.getService(Context.WINDOW_SERVICE);
- mIWindowManager = IWindowManager.Stub.asInterface(b);
- }
- return mIWindowManager;
- }
-
- private NotificationManager getNotificationManager() {
- if (mNotificationManager == null) {
- mNotificationManager =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
- return mNotificationManager;
- }
-
ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle) {
ActiveAdmin admin = getUserData(userHandle).mAdminMap.get(who);
if (admin != null
@@ -1208,7 +1318,7 @@
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
throws SecurityException {
- final int callingUid = Binder.getCallingUid();
+ final int callingUid = mInjector.binderGetCallingUid();
ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
if (result != null) {
@@ -1232,7 +1342,7 @@
+ admin.info.getTagForPolicy(reqPolicy));
} else {
throw new SecurityException("No active admin owned by uid "
- + Binder.getCallingUid() + " for policy #" + reqPolicy);
+ + mInjector.binderGetCallingUid() + " for policy #" + reqPolicy);
}
}
@@ -1248,7 +1358,7 @@
}
if (admin.getUid() != uid) {
throw new SecurityException("Admin " + who + " is not owned by uid "
- + Binder.getCallingUid());
+ + mInjector.binderGetCallingUid());
}
if (isActiveAdminWithPolicyForUserLocked(admin, reqPolicy, userId)) {
return admin;
@@ -1275,12 +1385,12 @@
&& !hasUserSetupCompleted(userId);
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
- if ((userId == UserHandle.USER_OWNER && (ownsDevice || ownsInitialization))
+ if ((userId == UserHandle.USER_SYSTEM && (ownsDevice || ownsInitialization))
|| (ownsDevice && ownsProfile)) {
return true;
}
} else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
- if ((userId == UserHandle.USER_OWNER && ownsDevice) || ownsProfile
+ if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || ownsProfile
|| ownsInitialization) {
return true;
}
@@ -1410,11 +1520,11 @@
}
}
- private static JournaledFile makeJournaledFile(int userHandle) {
- final String base = userHandle == 0
- ? "/data/system/" + DEVICE_POLICIES_XML
- : new File(Environment.getUserSystemDirectory(userHandle), DEVICE_POLICIES_XML)
- .getAbsolutePath();
+ private JournaledFile makeJournaledFile(int userHandle) {
+ final String base = userHandle == UserHandle.USER_SYSTEM
+ ? mInjector.getDevicePolicyFilePathForSystemUser() + DEVICE_POLICIES_XML
+ : new File(mInjector.environmentGetUserSystemDirectory(userHandle),
+ DEVICE_POLICIES_XML).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
@@ -1513,6 +1623,7 @@
journal.commit();
sendChangedNotification(userHandle);
} catch (IOException e) {
+ Slog.w(LOG_TAG, "failed writing file", e);
try {
if (stream != null) {
stream.close();
@@ -1527,11 +1638,11 @@
private void sendChangedNotification(int userHandle) {
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -1663,9 +1774,9 @@
// sufficiently what is currently set. Note that this is only
// a sanity check in case the two get out of sync; this should
// never normally happen.
- final long identity = Binder.clearCallingIdentity();
+ final long identity = mInjector.binderClearCallingIdentity();
try {
- LockPatternUtils utils = new LockPatternUtils(mContext);
+ LockPatternUtils utils = mInjector.newLockPatternUtils();
if (utils.getActivePasswordQuality(userHandle) < policy.mActivePasswordQuality) {
Slog.w(LOG_TAG, "Active password quality 0x"
+ Integer.toHexString(policy.mActivePasswordQuality)
@@ -1681,7 +1792,7 @@
policy.mActivePasswordNonLetter = 0;
}
} finally {
- Binder.restoreCallingIdentity(identity);
+ mInjector.binderRestoreCallingIdentity(identity);
}
validatePasswordOwnerLocked(policy);
@@ -1695,26 +1806,26 @@
}
private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
- IActivityManager am = ActivityManagerNative.getDefault();
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- am.updateLockTaskPackages(userId, packages.toArray(new String[packages.size()]));
+ mInjector.getIActivityManager()
+ .updateLockTaskPackages(userId, packages.toArray(new String[packages.size()]));
} catch (RemoteException e) {
// Not gonna happen.
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
private void updateDeviceOwnerLocked() {
- IActivityManager am = ActivityManagerNative.getDefault();
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- am.updateDeviceOwner(getDeviceOwner());
+ mInjector.getIActivityManager()
+ .updateDeviceOwner(getDeviceOwner());
} catch (RemoteException e) {
// Not gonna happen.
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -1759,17 +1870,17 @@
// Ensure the status of the camera is synced down to the system. Interested native services
// should monitor this value and act accordingly.
String cameraPropertyForUser = SYSTEM_PROP_DISABLE_CAMERA_PREFIX + policy.mUserHandle;
- boolean systemState = SystemProperties.getBoolean(cameraPropertyForUser, false);
+ boolean systemState = mInjector.systemPropertiesGetBoolean(cameraPropertyForUser, false);
boolean cameraDisabled = getCameraDisabled(null, policy.mUserHandle);
if (cameraDisabled != systemState) {
- long token = Binder.clearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
String value = cameraDisabled ? "1" : "0";
if (DBG) Slog.v(LOG_TAG, "Change in camera state ["
+ cameraPropertyForUser + "] = " + value);
- SystemProperties.set(cameraPropertyForUser, value);
+ mInjector.systemPropertiesSet(cameraPropertyForUser, value);
} finally {
- Binder.restoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
}
}
@@ -1789,8 +1900,8 @@
}
private void onLockSettingsReady() {
- getUserData(UserHandle.USER_OWNER);
- loadDeviceOwner();
+ getUserData(UserHandle.USER_SYSTEM);
+ loadOwners();
cleanUpOldUsers();
// Register an observer for watching for user setup complete.
new SetupContentObserver(mHandler).register(mContext.getContentResolver());
@@ -1809,14 +1920,13 @@
private void ensureDeviceOwnerUserStarted() {
if (mOwners.hasDeviceOwner()) {
- final IActivityManager am = ActivityManagerNative.getDefault();
final int userId = mOwners.getDeviceOwnerUserId();
if (VERBOSE_LOG) {
Log.v(LOG_TAG, "Starting non-system DO user: " + userId);
}
if (userId != UserHandle.USER_SYSTEM) {
try {
- am.startUserInBackground(userId);
+ mInjector.getIActivityManager().startUserInBackground(userId);
// STOPSHIP Prevent the DO user from being killed.
@@ -1917,7 +2027,7 @@
Log.e(LOG_TAG, "Could not connect to KeyChain service", e);
}
if (!hasCert) {
- getNotificationManager().cancelAsUser(
+ mInjector.getNotificationManager().cancelAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, userHandle);
return;
}
@@ -1962,7 +2072,7 @@
com.android.internal.R.color.system_notification_accent_color))
.build();
- getNotificationManager().notifyAsUser(
+ mInjector.getNotificationManager().notifyAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
}
}
@@ -1991,7 +2101,7 @@
throw new IllegalArgumentException("Bad admin: " + adminReceiver);
}
synchronized (this) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
if (!refreshing
&& getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
@@ -2018,7 +2128,7 @@
sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
onEnableData, null);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -2112,7 +2222,7 @@
if (admin == null) {
return;
}
- if (admin.getUid() != Binder.getCallingUid()) {
+ if (admin.getUid() != mInjector.binderGetCallingUid()) {
// Active device owners must remain active admins.
if (isDeviceOwner(adminReceiver.getPackageName())) {
return;
@@ -2120,11 +2230,11 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
}
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
removeActiveAdminLocked(adminReceiver, userHandle);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -2394,7 +2504,7 @@
|| activeAdmin.crossProfileWidgetProviders.isEmpty()) {
return null;
}
- if (Binder.getCallingUid() == Process.myUid()) {
+ if (mInjector.binderIsCallingUidMyUid()) {
return new ArrayList<>(activeAdmin.crossProfileWidgetProviders);
} else {
return activeAdmin.crossProfileWidgetProviders;
@@ -2959,7 +3069,7 @@
}
}
- int callingUid = Binder.getCallingUid();
+ int callingUid = mInjector.binderGetCallingUid();
DevicePolicyData policy = getUserData(userHandle);
if (policy.mPasswordOwner >= 0 && policy.mPasswordOwner != callingUid) {
Slog.w(LOG_TAG, "resetPassword: already set by another uid and not entered by user");
@@ -2975,7 +3085,7 @@
// Don't do this with the lock held, because it is going to call
// back in to the service.
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
LockPatternUtils utils = new LockPatternUtils(mContext);
if (!TextUtils.isEmpty(password)) {
@@ -2996,7 +3106,7 @@
}
}
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
return true;
@@ -3004,10 +3114,10 @@
private void setDoNotAskCredentialsOnBoot() {
synchronized (this) {
- DevicePolicyData policyData = getUserData(UserHandle.USER_OWNER);
+ DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
if (!policyData.doNotAskCredentialsOnBoot) {
policyData.doNotAskCredentialsOnBoot = true;
- saveSettingsLocked(UserHandle.USER_OWNER);
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
}
}
}
@@ -3017,7 +3127,7 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT, null);
synchronized (this) {
- DevicePolicyData policyData = getUserData(UserHandle.USER_OWNER);
+ DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
return policyData.doNotAskCredentialsOnBoot;
}
}
@@ -3046,7 +3156,7 @@
return;
}
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
if (timeMs <= 0) {
timeMs = Integer.MAX_VALUE;
@@ -3058,9 +3168,11 @@
}
policy.mLastMaximumTimeToLock = timeMs;
- mPowerManagerInternal.setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs);
+ // TODO It can overflow. Cap it.
+ mInjector.getPowerManagerInternal()
+ .setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -3112,26 +3224,21 @@
}
private void lockNowUnchecked() {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
// Power off the display
- mPowerManager.goToSleep(SystemClock.uptimeMillis(),
+ mInjector.powerManagerGoToSleep(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN, 0);
// Ensure the device is locked
new LockPatternUtils(mContext).requireStrongAuth(
STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, UserHandle.USER_ALL);
- getWindowManager().lockNow(null);
+ mInjector.getIWindowManager().lockNow(null);
} catch (RemoteException e) {
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
- private boolean isExtStorageEncrypted() {
- String state = SystemProperties.get("vold.decrypt");
- return !"".equals(state);
- }
-
@Override
public void enforceCanManageCaCerts(ComponentName who) {
if (who == null) {
@@ -3146,7 +3253,7 @@
}
private boolean isCallerDelegatedCertInstaller() {
- final int callingUid = Binder.getCallingUid();
+ final int callingUid = mInjector.binderGetCallingUid();
final int userHandle = UserHandle.getUserId(callingUid);
synchronized (this) {
final DevicePolicyData policy = getUserData(userHandle);
@@ -3181,7 +3288,7 @@
}
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
- final long id = Binder.clearCallingIdentity();
+ final long id = mInjector.binderClearCallingIdentity();
try {
final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
try {
@@ -3196,7 +3303,7 @@
Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
Thread.currentThread().interrupt();
} finally {
- Binder.restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return false;
}
@@ -3212,7 +3319,7 @@
enforceCanManageCaCerts(admin);
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
- final long id = Binder.clearCallingIdentity();
+ final long id = mInjector.binderClearCallingIdentity();
try {
final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
try {
@@ -3228,7 +3335,7 @@
Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
Thread.currentThread().interrupt();
} finally {
- Binder.restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -3244,7 +3351,7 @@
}
}
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
- final long id = Binder.clearCallingIdentity();
+ final long id = mInjector.binderClearCallingIdentity();
try {
final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
try {
@@ -3259,7 +3366,7 @@
Log.w(LOG_TAG, "Interrupted while installing certificate", e);
Thread.currentThread().interrupt();
} finally {
- Binder.restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return false;
}
@@ -3268,11 +3375,11 @@
public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
final IBinder response) {
// Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
- if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+ if (UserHandle.getAppId(mInjector.binderGetCallingUid()) != Process.SYSTEM_UID) {
return;
}
- final UserHandle caller = Binder.getCallingUserHandle();
+ final UserHandle caller = mInjector.binderGetCallingUserHandle();
// If there is a profile owner, redirect to that; otherwise query the device owner.
ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
if (aliasChooser == null && caller.isOwner()) {
@@ -3293,7 +3400,7 @@
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS, alias);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE, response);
- final long id = Binder.clearCallingIdentity();
+ final long id = mInjector.binderClearCallingIdentity();
try {
mContext.sendOrderedBroadcastAsUser(intent, caller, null, new BroadcastReceiver() {
@Override
@@ -3303,7 +3410,7 @@
}
}, null, Activity.RESULT_OK, null, null);
} finally {
- Binder.restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -3372,20 +3479,14 @@
final ActiveAdmin admin = getActiveAdminForCallerLocked(null,
DeviceAdminInfo.USES_POLICY_WIPE_DATA);
- final String source;
- final ComponentName cname = admin.info.getComponent();
- if (cname != null) {
- source = cname.flattenToShortString();
- } else {
- source = admin.info.getPackageName();
- }
+ final String source = admin.info.getComponent().flattenToShortString();
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
&& !hasUserSetupCompleted(userHandle);
- if (userHandle != UserHandle.USER_OWNER
+ if (userHandle != UserHandle.USER_SYSTEM
|| !(isDeviceOwner(admin.info.getPackageName())
|| ownsInitialization)) {
throw new SecurityException(
@@ -3401,22 +3502,22 @@
wipeDeviceOrUserLocked(wipeExtRequested, userHandle,
"DevicePolicyManager.wipeData() from " + source);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
private void wipeDeviceOrUserLocked(boolean wipeExtRequested, final int userHandle, String reason) {
- if (userHandle == UserHandle.USER_OWNER) {
+ if (userHandle == UserHandle.USER_SYSTEM) {
wipeDataLocked(wipeExtRequested, reason);
} else {
mHandler.post(new Runnable() {
@Override
public void run() {
try {
- IActivityManager am = ActivityManagerNative.getDefault();
+ IActivityManager am = mInjector.getIActivityManager();
if (am.getCurrentUser().id == userHandle) {
- am.switchUser(UserHandle.USER_OWNER);
+ am.switchUser(UserHandle.USER_SYSTEM);
}
boolean isManagedProfile = isManagedProfile(userHandle);
@@ -3442,11 +3543,11 @@
.setColor(mContext.getColor(R.color.system_notification_accent_color))
.setStyle(new Notification.BigTextStyle().bigText(contentText))
.build();
- getNotificationManager().notify(PROFILE_WIPED_NOTIFICATION_ID, notification);
+ mInjector.getNotificationManager().notify(PROFILE_WIPED_NOTIFICATION_ID, notification);
}
private void clearWipeProfileNotification() {
- getNotificationManager().cancel(PROFILE_WIPED_NOTIFICATION_ID);
+ mInjector.getNotificationManager().cancel(PROFILE_WIPED_NOTIFICATION_ID);
}
@Override
@@ -3506,7 +3607,7 @@
|| p.mActivePasswordNumeric != numbers
|| p.mActivePasswordSymbols != symbols
|| p.mActivePasswordNonLetter != nonletter) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
p.mActivePasswordQuality = quality;
p.mActivePasswordLength = length;
@@ -3524,7 +3625,7 @@
DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -3560,7 +3661,7 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
boolean wipeData = false;
int identifier = 0;
@@ -3591,7 +3692,7 @@
"reportFailedPasswordAttempt()");
}
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -3604,7 +3705,7 @@
synchronized (this) {
DevicePolicyData policy = getUserData(userHandle);
if (policy.mFailedPasswordAttempts != 0 || policy.mPasswordOwner >= 0) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
policy.mFailedPasswordAttempts = 0;
policy.mPasswordOwner = -1;
@@ -3615,7 +3716,7 @@
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
}
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -3630,8 +3731,8 @@
synchronized(this) {
Preconditions.checkNotNull(who, "ComponentName is null");
- // Only check if owner has set global proxy. We don't allow other users to set it.
- DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+ // Only check if system user has set global proxy. We don't allow other users to set it.
+ DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
ActiveAdmin admin = getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
@@ -3647,8 +3748,8 @@
}
}
- // If the user is not the owner, don't set the global proxy. Fail silently.
- if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
+ // If the user is not system, don't set the global proxy. Fail silently.
+ if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
Slog.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
+ UserHandle.getCallingUserId() + " is not permitted.");
return null;
@@ -3666,11 +3767,11 @@
// Reset the global proxy accordingly
// Do this using system permissions, as apps cannot write to secure settings
- long origId = Binder.clearCallingIdentity();
+ long origId = mInjector.binderClearCallingIdentity();
try {
resetGlobalProxyLocked(policy);
} finally {
- Binder.restoreCallingIdentity(origId);
+ mInjector.binderRestoreCallingIdentity(origId);
}
return null;
}
@@ -3683,7 +3784,7 @@
}
enforceCrossUserPermission(userHandle);
synchronized(this) {
- DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+ DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
// Scan through active admins and find if anyone has already
// set the global proxy.
final int N = policy.mAdminList.size();
@@ -3705,13 +3806,13 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
}
- long token = Binder.clearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
ConnectivityManager connectivityManager = (ConnectivityManager)
mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.setGlobalProxy(proxyInfo);
} finally {
- Binder.restoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
}
@@ -3771,10 +3872,9 @@
final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
// Check for permissions
- // Only owner can set storage encryption
- if (userHandle != UserHandle.USER_OWNER
- || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
- Slog.w(LOG_TAG, "Only owner is allowed to set storage encryption. User "
+ // Only system user can set storage encryption
+ if (userHandle != UserHandle.USER_SYSTEM) {
+ Slog.w(LOG_TAG, "Only owner/system user is allowed to set storage encryption. User "
+ UserHandle.getCallingUserId() + " is not permitted.");
return 0;
}
@@ -3793,7 +3893,7 @@
saveSettingsLocked(userHandle);
}
- DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+ DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
// (2) Compute "max" for all admins
boolean newRequested = false;
final int N = policy.mAdminList.size();
@@ -3873,15 +3973,15 @@
* {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}.
*/
private int getEncryptionStatus() {
- String status = SystemProperties.get("ro.crypto.state", "unsupported");
+ String status = mInjector.systemPropertiesGet("ro.crypto.state", "unsupported");
if ("encrypted".equalsIgnoreCase(status)) {
- final long token = Binder.clearCallingIdentity();
+ final long token = mInjector.binderClearCallingIdentity();
try {
return LockPatternUtils.isDeviceEncrypted()
? DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
: DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY;
} finally {
- Binder.restoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
} else if ("unencrypted".equalsIgnoreCase(status)) {
return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
@@ -3946,13 +4046,13 @@
}
private void updateScreenCaptureDisabledInWindowManager(int userHandle, boolean disabled) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- getWindowManager().setScreenCaptureDisabled(userHandle, disabled);
+ mInjector.getIWindowManager().setScreenCaptureDisabled(userHandle, disabled);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -3977,12 +4077,12 @@
// Turn AUTO_TIME on in settings if it is required
if (required) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -4091,7 +4191,7 @@
return 0;
}
enforceCrossUserPermission(userHandle);
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (this) {
if (who != null) {
@@ -4134,7 +4234,7 @@
return which;
}
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -4144,7 +4244,7 @@
return false;
}
if (packageName == null
- || !Owners.isInstalledForUser(packageName, userId)) {
+ || !isPackageInstalledForUser(packageName, userId)) {
throw new IllegalArgumentException("Invalid package name " + packageName
+ " for device owner");
}
@@ -4152,15 +4252,13 @@
enforceCanSetDeviceOwner(userId);
// Shutting down backup manager service permanently.
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- IBackupManager ibm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- ibm.setBackupServiceActive(UserHandle.USER_OWNER, false);
+ mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, false);
} catch (RemoteException e) {
throw new IllegalStateException("Failed deactivating backup service.", e);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
mOwners.setDeviceOwner(packageName, ownerName, userId);
@@ -4168,12 +4266,12 @@
updateDeviceOwnerLocked();
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
- ident = Binder.clearCallingIdentity();
+ ident = mInjector.binderClearCallingIdentity();
try {
// TODO Send to system too?
mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
return true;
}
@@ -4205,13 +4303,16 @@
if (!mHasFeature) {
return null;
}
+ // TODO: Do we really need it? getDeviceOwner() doesn't require it.
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
synchronized (this) {
if (!mOwners.hasDeviceOwner()) {
return null;
}
+ // TODO This totally ignores the name passed to setDeviceOwner (change for b/20679292)
+ // Should setDeviceOwner/ProfileOwner still take a name?
String deviceOwnerPackage = mOwners.getDeviceOwnerPackageName();
- return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_OWNER);
+ return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_SYSTEM);
}
}
@@ -4222,7 +4323,7 @@
return null;
}
- DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+ DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
final int n = policy.mAdminList.size();
for (int i = 0; i < n; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -4238,7 +4339,7 @@
Preconditions.checkNotNull(packageName, "packageName is null");
try {
int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
- if (uid != Binder.getCallingUid()) {
+ if (uid != mInjector.binderGetCallingUid()) {
throw new SecurityException("Invalid packageName");
}
} catch (NameNotFoundException e) {
@@ -4248,21 +4349,19 @@
throw new SecurityException("clearDeviceOwner can only be called by the device owner");
}
synchronized (this) {
- clearUserPoliciesLocked(new UserHandle(UserHandle.USER_OWNER));
+ clearUserPoliciesLocked(new UserHandle(UserHandle.USER_SYSTEM));
mOwners.clearDeviceOwner();
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
// Reactivate backup service.
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- IBackupManager ibm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- ibm.setBackupServiceActive(UserHandle.USER_OWNER, true);
+ mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, true);
} catch (RemoteException e) {
throw new IllegalStateException("Failed reactivating backup service.", e);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -4274,15 +4373,16 @@
}
if (initializer == null ||
!mOwners.hasDeviceOwner() ||
- !Owners.isInstalledForUser(initializer.getPackageName(),
+ !isPackageInstalledForUser(initializer.getPackageName(),
mOwners.getDeviceOwnerUserId())) {
throw new IllegalArgumentException("Invalid component name " + initializer
+ " for device initializer or no device owner set");
}
boolean isInitializerSystemApp;
try {
- isInitializerSystemApp = isSystemApp(AppGlobals.getPackageManager(),
- initializer.getPackageName(), Binder.getCallingUserHandle().getIdentifier());
+ isInitializerSystemApp = isSystemApp(mIPackageManager,
+ initializer.getPackageName(),
+ mInjector.binderGetCallingUserHandle().getIdentifier());
} catch (RemoteException | IllegalArgumentException e) {
isInitializerSystemApp = false;
Slog.e(LOG_TAG, "Fail to check if device initialzer is system app.", e);
@@ -4365,9 +4465,9 @@
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, UserHandle.getCallingUserId());
- if (admin.getUid() != Binder.getCallingUid()) {
+ if (admin.getUid() != mInjector.binderGetCallingUid()) {
throw new SecurityException("Admin " + who + " is not owned by uid "
- + Binder.getCallingUid());
+ + mInjector.binderGetCallingUid());
}
if (!isDeviceInitializer(admin.info.getPackageName())
@@ -4376,12 +4476,12 @@
"clearDeviceInitializer can only be called by the device initializer/owner");
}
synchronized (this) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
mOwners.clearDeviceInitializer();
mOwners.writeDeviceOwner();
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -4392,7 +4492,7 @@
return false;
}
if (who == null
- || !Owners.isInstalledForUser(who.getPackageName(), userHandle)) {
+ || !isPackageInstalledForUser(who.getPackageName(), userHandle)) {
throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
@@ -4409,7 +4509,7 @@
if (!mHasFeature) {
return;
}
- UserHandle callingUser = Binder.getCallingUserHandle();
+ UserHandle callingUser = mInjector.binderGetCallingUserHandle();
// Check if this is the profile owner who is calling
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
synchronized (this) {
@@ -4429,15 +4529,15 @@
policy.mStatusBarDisabled = false;
saveSettingsLocked(userId);
- final long ident = Binder.clearCallingIdentity();
+ final long ident = mInjector.binderClearCallingIdentity();
try {
clearUserRestrictions(userHandle);
- AppGlobals.getPackageManager().updatePermissionFlagsForAllApps(
+ mIPackageManager.updatePermissionFlagsForAllApps(
PackageManager.FLAG_PERMISSION_POLICY_FIXED,
0 /* flagValues */, userHandle.getIdentifier());
} catch (RemoteException re) {
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -4445,11 +4545,9 @@
private void clearUserRestrictions(UserHandle userHandle) {
Bundle userRestrictions = mUserManager.getUserRestrictions();
mUserManager.setUserRestrictions(new Bundle(), userHandle);
- IAudioService iAudioService = IAudioService.Stub.asInterface(
- ServiceManager.getService(Context.AUDIO_SERVICE));
if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
try {
- iAudioService.setMasterMute(true, 0, mContext.getPackageName(),
+ mInjector.getIAudioService().setMasterMute(true, 0, mContext.getPackageName(),
userHandle.getIdentifier());
} catch (RemoteException e) {
// Not much we can do here.
@@ -4457,7 +4555,7 @@
}
if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
try {
- iAudioService.setMicrophoneMute(true, mContext.getPackageName(),
+ mInjector.getIAudioService().setMicrophoneMute(true, mContext.getPackageName(),
userHandle.getIdentifier());
} catch (RemoteException e) {
// Not much we can do here.
@@ -4474,9 +4572,7 @@
if (!mHasFeature) {
return true;
}
- DevicePolicyData policy = getUserData(userHandle);
- // If policy is null, return true, else check if the setup has completed.
- return policy == null || policy.mUserSetupComplete;
+ return getUserData(userHandle).mUserSetupComplete;
}
@Override
@@ -4497,18 +4593,17 @@
"This method can only be called by device initializers");
}
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
if (!isDeviceOwner(activeAdmin.info.getPackageName())) {
- IPackageManager ipm = AppGlobals.getPackageManager();
- ipm.setComponentEnabledSetting(who,
+ mIPackageManager.setComponentEnabledSetting(who,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP, userId);
removeActiveAdmin(who, userId);
}
- if (userId == UserHandle.USER_OWNER) {
+ if (userId == UserHandle.USER_SYSTEM) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 1);
}
@@ -4518,7 +4613,7 @@
Log.i(LOG_TAG, "Can't talk to package manager", e);
return false;
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return true;
}
@@ -4536,7 +4631,7 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setUserEnabled(userId);
UserInfo parent = mUserManager.getProfileParent(userId);
@@ -4546,7 +4641,7 @@
Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, new UserHandle(parent.id));
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -4558,11 +4653,11 @@
// Check if this is the profile owner (includes device owner).
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setUserName(userId, profileName);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -4612,7 +4707,7 @@
* Canonical name for a given package.
*/
private String getApplicationLabel(String packageName, int userHandle) {
- long token = Binder.clearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
final Context userContext;
try {
@@ -4630,7 +4725,7 @@
}
return result != null ? result.toString() : null;
} finally {
- Binder.restoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
}
@@ -4656,7 +4751,7 @@
throw new IllegalStateException("Trying to set the profile owner, but profile owner "
+ "is already set.");
}
- int callingUid = Binder.getCallingUid();
+ int callingUid = mInjector.binderGetCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
if (hasUserSetupCompleted(userHandle) &&
AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) {
@@ -4685,14 +4780,17 @@
throw new IllegalStateException("Trying to set the device owner, but device owner "
+ "is already set.");
}
- // STOPSHIP Make sure the DO user is running
- int callingUid = Binder.getCallingUid();
+ if (!mUserManager.isUserRunning(new UserHandle(userId))) {
+ throw new IllegalStateException("User not running: " + userId);
+ }
+
+ int callingUid = mInjector.binderGetCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
- if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+ if (!hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
return;
}
// STOPSHIP Do proper check in split user mode
- if (!UserManager.isSplitSystemUser()) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
if (mUserManager.getUserCount() > 1) {
throw new IllegalStateException(
"Not allowed to set the device owner because there "
@@ -4705,18 +4803,14 @@
}
}
return;
- } else {
- // STOPSHIP check the caller UID with userId
}
- if (mUserManager.isUserRunning(new UserHandle(userId))) {
- throw new IllegalStateException("User not running: " + userId);
- }
+ // STOPSHIP check the caller UID with userId
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
// STOPSHIP Do proper check in split user mode
- if (!UserManager.isSplitSystemUser()) {
- if (hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
+ if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
throw new IllegalStateException("Cannot set the device owner if the device is "
+ "already set-up");
}
@@ -4727,7 +4821,7 @@
if (userHandle < 0) {
throw new IllegalArgumentException("Invalid userId " + userHandle);
}
- final int callingUid = Binder.getCallingUid();
+ final int callingUid = mInjector.binderGetCallingUid();
if (userHandle == UserHandle.getUserId(callingUid)) return;
if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
mContext.enforceCallingOrSelfPermission(
@@ -4743,32 +4837,31 @@
}
private UserInfo getProfileParent(int userHandle) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
return mUserManager.getProfileParent(userHandle);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
private boolean isManagedProfile(int userHandle) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
return mUserManager.getUserInfo(userHandle).isManagedProfile();
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
private void enableIfNecessary(String packageName, int userId) {
try {
- IPackageManager ipm = AppGlobals.getPackageManager();
- ApplicationInfo ai = ipm.getApplicationInfo(packageName,
+ ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
userId);
if (ai.enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
- ipm.setApplicationEnabledSetting(packageName,
+ mIPackageManager.setApplicationEnabledSetting(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.DONT_KILL_APP, userId, "DevicePolicyManager");
}
@@ -4782,8 +4875,8 @@
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump DevicePolicyManagerService from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
+ + mInjector.binderGetCallingPid()
+ + ", uid=" + mInjector.binderGetCallingUid());
return;
}
@@ -4824,14 +4917,13 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- IPackageManager pm = AppGlobals.getPackageManager();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- pm.addPersistentPreferredActivity(filter, activity, userHandle);
+ mIPackageManager.addPersistentPreferredActivity(filter, activity, userHandle);
} catch (RemoteException re) {
// Shouldn't happen
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -4843,14 +4935,13 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- IPackageManager pm = AppGlobals.getPackageManager();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- pm.clearPackagePersistentPreferredActivities(packageName, userHandle);
+ mIPackageManager.clearPackagePersistentPreferredActivities(packageName, userHandle);
} catch (RemoteException re) {
// Shouldn't happen
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -4862,11 +4953,11 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -4964,7 +5055,7 @@
@Override
public ComponentName getRestrictionsProvider(int userHandle) {
synchronized (this) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ if (mInjector.binderGetCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system can query the permission provider");
}
DevicePolicyData userData = getUserData(userHandle);
@@ -4979,8 +5070,7 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- IPackageManager pm = AppGlobals.getPackageManager();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo parent = mUserManager.getProfileParent(callingUserId);
if (parent == null) {
@@ -4989,17 +5079,17 @@
return;
}
if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) {
- pm.addCrossProfileIntentFilter(filter, who.getPackageName(), callingUserId,
- parent.id, 0);
+ mIPackageManager.addCrossProfileIntentFilter(
+ filter, who.getPackageName(), callingUserId, parent.id, 0);
}
if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) {
- pm.addCrossProfileIntentFilter(filter, who.getPackageName(),
+ mIPackageManager.addCrossProfileIntentFilter(filter, who.getPackageName(),
parent.id, callingUserId, 0);
}
} catch (RemoteException re) {
// Shouldn't happen
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5010,8 +5100,7 @@
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- IPackageManager pm = AppGlobals.getPackageManager();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo parent = mUserManager.getProfileParent(callingUserId);
if (parent == null) {
@@ -5020,15 +5109,16 @@
return;
}
// Removing those that go from the managed profile to the parent.
- pm.clearCrossProfileIntentFilters(callingUserId, who.getPackageName());
+ mIPackageManager.clearCrossProfileIntentFilters(
+ callingUserId, who.getPackageName());
// And those that go from the parent to the managed profile.
// If we want to support multiple managed profiles, we will have to only remove
// those that have callingUserId as their target.
- pm.clearCrossProfileIntentFilters(parent.id, who.getPackageName());
+ mIPackageManager.clearCrossProfileIntentFilters(parent.id, who.getPackageName());
} catch (RemoteException re) {
// Shouldn't happen
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5040,7 +5130,7 @@
private boolean checkPackagesInPermittedListOrSystem(List<String> enabledPackages,
List<String> permittedList) {
int userIdToCheck = UserHandle.getCallingUserId();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
// If we have an enabled packages list for a managed profile the packages
// we should check are installed for the parent user.
@@ -5049,12 +5139,11 @@
userIdToCheck = user.profileGroupId;
}
- IPackageManager pm = AppGlobals.getPackageManager();
for (String enabledPackage : enabledPackages) {
boolean systemService = false;
try {
- ApplicationInfo applicationInfo = pm.getApplicationInfo(enabledPackage,
- PackageManager.GET_UNINSTALLED_PACKAGES, userIdToCheck);
+ ApplicationInfo applicationInfo = mIPackageManager.getApplicationInfo(
+ enabledPackage, PackageManager.GET_UNINSTALLED_PACKAGES, userIdToCheck);
systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
} catch (RemoteException e) {
Log.i(LOG_TAG, "Can't talk to package managed", e);
@@ -5064,7 +5153,7 @@
}
}
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return true;
}
@@ -5089,7 +5178,7 @@
if (packageList != null) {
int userId = UserHandle.getCallingUserId();
List<AccessibilityServiceInfo> enabledServices = null;
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo user = mUserManager.getUserInfo(userId);
if (user.isManagedProfile()) {
@@ -5099,7 +5188,7 @@
enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
if (enabledServices != null) {
@@ -5170,7 +5259,7 @@
// If we have a permitted list add all system accessibility services.
if (result != null) {
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo user = mUserManager.getUserInfo(userId);
if (user.isManagedProfile()) {
@@ -5181,7 +5270,6 @@
List<AccessibilityServiceInfo> installedServices =
accessibilityManager.getInstalledAccessibilityServiceList();
- IPackageManager pm = AppGlobals.getPackageManager();
if (installedServices != null) {
for (AccessibilityServiceInfo service : installedServices) {
ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
@@ -5192,7 +5280,7 @@
}
}
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -5202,12 +5290,12 @@
private boolean checkCallerIsCurrentUserOrProfile() {
int callingUserId = UserHandle.getCallingUserId();
- long token = Binder.clearCallingIdentity();
+ long token = mInjector.binderClearCallingIdentity();
try {
UserInfo currentUser;
UserInfo callingUser = mUserManager.getUserInfo(callingUserId);
try {
- currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+ currentUser = mInjector.getIActivityManager().getCurrentUser();
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to talk to activity managed.", e);
return false;
@@ -5224,7 +5312,7 @@
return false;
}
} finally {
- Binder.restoreCallingIdentity(token);
+ mInjector.binderRestoreCallingIdentity(token);
}
return true;
}
@@ -5290,7 +5378,7 @@
public List getPermittedInputMethodsForCurrentUser() {
UserInfo currentUser;
try {
- currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+ currentUser = mInjector.getIActivityManager().getCurrentUser();
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to make remote calls to get current user", e);
// Activity managed is dead, just allow all IMEs
@@ -5328,9 +5416,8 @@
InputMethodManager inputMethodManager = (InputMethodManager) mContext
.getSystemService(Context.INPUT_METHOD_SERVICE);
List<InputMethodInfo> imes = inputMethodManager.getInputMethodList();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = AppGlobals.getPackageManager();
if (imes != null) {
for (InputMethodInfo ime : imes) {
ServiceInfo serviceInfo = ime.getServiceInfo();
@@ -5341,7 +5428,7 @@
}
}
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
return result;
@@ -5354,7 +5441,7 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserInfo userInfo = mUserManager.createUser(name, 0 /* flags */);
if (userInfo != null) {
@@ -5362,7 +5449,7 @@
}
return null;
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5374,21 +5461,19 @@
if (user == null) {
return null;
}
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
String profileOwnerPkg = profileOwnerComponent.getPackageName();
- final IPackageManager ipm = AppGlobals.getPackageManager();
- IActivityManager activityManager = ActivityManagerNative.getDefault();
final int userHandle = user.getIdentifier();
try {
// Install the profile owner if not present.
- if (!ipm.isPackageAvailable(profileOwnerPkg, userHandle)) {
- ipm.installExistingPackageAsUser(profileOwnerPkg, userHandle);
+ if (!mIPackageManager.isPackageAvailable(profileOwnerPkg, userHandle)) {
+ mIPackageManager.installExistingPackageAsUser(profileOwnerPkg, userHandle);
}
// Start user in background.
- activityManager.startUserInBackground(userHandle);
+ mInjector.getIActivityManager().startUserInBackground(userHandle);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to make remote calls for configureUser", e);
}
@@ -5397,7 +5482,7 @@
setProfileOwner(profileOwnerComponent, ownerName, userHandle);
return user;
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -5407,11 +5492,11 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
return mUserManager.removeUser(userHandle.getIdentifier());
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5422,18 +5507,18 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- int userId = UserHandle.USER_OWNER;
+ int userId = UserHandle.USER_SYSTEM;
if (userHandle != null) {
userId = userHandle.getIdentifier();
}
- return ActivityManagerNative.getDefault().switchUser(userId);
+ return mInjector.getIActivityManager().switchUser(userId);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Couldn't switch user", e);
return false;
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5446,14 +5531,14 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
// if no restrictions were saved, mUserManager.getApplicationRestrictions
// returns null, but DPM method should return an empty Bundle as per JavaDoc
return bundle != null ? bundle : Bundle.EMPTY;
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5461,13 +5546,13 @@
@Override
public void setUserRestriction(ComponentName who, String key, boolean enabled) {
Preconditions.checkNotNull(who, "ComponentName is null");
- final UserHandle user = new UserHandle(UserHandle.getCallingUserId());
- final int userHandle = user.getIdentifier();
+ final int userHandle = UserHandle.getCallingUserId();
+ final UserHandle user = new UserHandle(userHandle);
synchronized (this) {
ActiveAdmin activeAdmin =
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
boolean isDeviceOwner = isDeviceOwner(activeAdmin.info.getPackageName());
- if (!isDeviceOwner && userHandle != UserHandle.USER_OWNER
+ if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
&& DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
throw new SecurityException("Profile owners cannot set user restriction " + key);
}
@@ -5476,24 +5561,16 @@
}
boolean alreadyRestricted = mUserManager.hasUserRestriction(key, user);
- IAudioService iAudioService = null;
- if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)
- || UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService = IAudioService.Stub.asInterface(
- ServiceManager.getService(Context.AUDIO_SERVICE));
- }
-
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
if (enabled && !alreadyRestricted) {
if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- iAudioService.setMicrophoneMute(true, mContext.getPackageName(),
- userHandle);
+ mInjector.getIAudioService()
+ .setMicrophoneMute(true, mContext.getPackageName(), userHandle);
} else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService.setMasterMute(true, 0, mContext.getPackageName(),
- userHandle);
- }
- if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
+ mInjector.getIAudioService()
+ .setMasterMute(true, 0, mContext.getPackageName(), userHandle);
+ } else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
userHandle);
@@ -5505,8 +5582,9 @@
Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
userHandle);
} else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
- // Only disable adb if changing for primary user, since it is global
- if (userHandle == UserHandle.USER_OWNER) {
+ // Only disable adb if changing for system user, since it is global
+ // TODO: should this be admin user?
+ if (userHandle == UserHandle.USER_SYSTEM) {
Settings.Global.putStringForUser(mContext.getContentResolver(),
Settings.Global.ADB_ENABLED, "0", userHandle);
}
@@ -5529,8 +5607,8 @@
// Send out notifications however as some clients may want to reread the
// value which actually changed due to a restriction having been applied.
final String property = Settings.Secure.SYS_PROP_SETTING_VERSION;
- long version = SystemProperties.getLong(property, 0) + 1;
- SystemProperties.set(property, Long.toString(version));
+ long version = mInjector.systemPropertiesGetLong(property, 0) + 1;
+ mInjector.systemPropertiesSet(property, Long.toString(version));
final String name = Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
Uri url = Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
@@ -5539,17 +5617,17 @@
}
if (!enabled && alreadyRestricted) {
if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- iAudioService.setMicrophoneMute(false, mContext.getPackageName(),
- userHandle);
+ mInjector.getIAudioService()
+ .setMicrophoneMute(false, mContext.getPackageName(), userHandle);
} else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService.setMasterMute(false, 0, mContext.getPackageName(),
- userHandle);
+ mInjector.getIAudioService()
+ .setMasterMute(false, 0, mContext.getPackageName(), userHandle);
}
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
sendChangedNotification(userHandle);
}
@@ -5563,15 +5641,15 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = AppGlobals.getPackageManager();
- return pm.setApplicationHiddenSettingAsUser(packageName, hidden, callingUserId);
+ return mIPackageManager.setApplicationHiddenSettingAsUser(
+ packageName, hidden, callingUserId);
} catch (RemoteException re) {
// shouldn't happen
Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return false;
}
@@ -5584,15 +5662,15 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = AppGlobals.getPackageManager();
- return pm.getApplicationHiddenSettingAsUser(packageName, callingUserId);
+ return mIPackageManager.getApplicationHiddenSettingAsUser(
+ packageName, callingUserId);
} catch (RemoteException re) {
// shouldn't happen
Slog.e(LOG_TAG, "Failed to getApplicationHiddenSettingAsUser", re);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
return false;
}
@@ -5607,7 +5685,7 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
if (DBG) {
@@ -5623,19 +5701,18 @@
primaryUser = um.getUserInfo(userId);
}
- IPackageManager pm = AppGlobals.getPackageManager();
- if (!isSystemApp(pm, packageName, primaryUser.id)) {
+ if (!isSystemApp(mIPackageManager, packageName, primaryUser.id)) {
throw new IllegalArgumentException("Only system apps can be enabled this way.");
}
// Install the app.
- pm.installExistingPackageAsUser(packageName, userId);
+ mIPackageManager.installExistingPackageAsUser(packageName, userId);
} catch (RemoteException re) {
// shouldn't happen
Slog.wtf(LOG_TAG, "Failed to install " + packageName, re);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5649,7 +5726,7 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
UserManager um = UserManager.get(mContext);
@@ -5660,8 +5737,8 @@
primaryUser = um.getUserInfo(userId);
}
- IPackageManager pm = AppGlobals.getPackageManager();
- List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent,
+ List<ResolveInfo> activitiesToEnable = mIPackageManager.queryIntentActivities(
+ intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
0, // no flags
primaryUser.id);
@@ -5672,9 +5749,9 @@
for (ResolveInfo info : activitiesToEnable) {
if (info.activityInfo != null) {
String packageName = info.activityInfo.packageName;
- if (isSystemApp(pm, packageName, primaryUser.id)) {
+ if (isSystemApp(mIPackageManager, packageName, primaryUser.id)) {
numberOfAppsInstalled++;
- pm.installExistingPackageAsUser(packageName, userId);
+ mIPackageManager.installExistingPackageAsUser(packageName, userId);
} else {
Slog.d(LOG_TAG, "Not enabling " + packageName + " since is not a"
+ " system app");
@@ -5688,7 +5765,7 @@
Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
return 0;
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5754,15 +5831,14 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = AppGlobals.getPackageManager();
- pm.setBlockUninstallForUser(packageName, uninstallBlocked, userId);
+ mIPackageManager.setBlockUninstallForUser(packageName, uninstallBlocked, userId);
} catch (RemoteException re) {
// Shouldn't happen.
Slog.e(LOG_TAG, "Failed to setBlockUninstallForUser", re);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -5779,15 +5855,14 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
}
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
- IPackageManager pm = AppGlobals.getPackageManager();
- return pm.getBlockUninstallForUser(packageName, userId);
+ return mIPackageManager.getBlockUninstallForUser(packageName, userId);
} catch (RemoteException re) {
// Shouldn't happen.
Slog.e(LOG_TAG, "Failed to getBlockUninstallForUser", re);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
return false;
@@ -5839,7 +5914,7 @@
actualLookupKey, actualContactId, originalIntent);
final int callingUserId = UserHandle.getCallingUserId();
- final long ident = Binder.clearCallingIdentity();
+ final long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (this) {
final int managedUserId = getManagedUserId(callingUserId);
@@ -5857,7 +5932,7 @@
mContext, intent, new UserHandle(managedUserId));
}
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
@@ -5938,7 +6013,7 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- int userHandle = Binder.getCallingUserHandle().getIdentifier();
+ int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
}
}
@@ -5960,7 +6035,7 @@
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- int userHandle = Binder.getCallingUserHandle().getIdentifier();
+ int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
final List<String> packages = getLockTaskPackagesLocked(userHandle);
return packages.toArray(new String[packages.size()]);
}
@@ -5979,7 +6054,7 @@
@Override
public boolean isLockTaskPermitted(String pkg) {
// Get current user's devicepolicy
- int uid = Binder.getCallingUid();
+ int uid = mInjector.binderGetCallingUid();
int userHandle = UserHandle.getUserId(uid);
DevicePolicyData policy = getUserData(userHandle);
synchronized (this) {
@@ -5998,7 +6073,7 @@
@Override
public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ if (mInjector.binderGetCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("notifyLockTaskModeChanged can only be called by system");
}
synchronized (this) {
@@ -6049,11 +6124,11 @@
}
}
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
Settings.Global.putString(contentResolver, setting, value);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -6078,11 +6153,11 @@
"Permission denial: Profile owners cannot update %1$s", setting));
}
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
Settings.Secure.putStringForUser(contentResolver, setting, value, callingUserId);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -6093,7 +6168,7 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long identity = Binder.clearCallingIdentity();
+ long identity = mInjector.binderClearCallingIdentity();
try {
IAudioService iAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
@@ -6101,7 +6176,7 @@
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to setMasterMute", re);
} finally {
- Binder.restoreCallingIdentity(identity);
+ mInjector.binderRestoreCallingIdentity(identity);
}
}
}
@@ -6125,11 +6200,11 @@
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
- long id = Binder.clearCallingIdentity();
+ long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setUserIcon(userId, icon);
} finally {
- restoreCallingIdentity(id);
+ mInjector.binderRestoreCallingIdentity(id);
}
}
}
@@ -6143,7 +6218,7 @@
final int userId = UserHandle.getCallingUserId();
LockPatternUtils utils = new LockPatternUtils(mContext);
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
// disallow disabling the keyguard if a password is currently set
if (disabled && utils.isSecure(userId)) {
@@ -6151,7 +6226,7 @@
}
utils.setLockScreenDisabled(disabled, userId);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
return true;
}
@@ -6174,7 +6249,7 @@
}
private boolean setStatusBarDisabledInternal(boolean disabled, int userId) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(
ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
@@ -6188,7 +6263,7 @@
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to disable the status bar", e);
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
return false;
}
@@ -6395,8 +6470,8 @@
mContext.enforceCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE,
"Only the system update service can broadcast update information");
- if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
- Slog.w(LOG_TAG, "Only the system update service in the primary user " +
+ if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
+ Slog.w(LOG_TAG, "Only the system update service in the system user " +
"can broadcast update information.");
return;
}
@@ -6419,7 +6494,7 @@
Log.e(LOG_TAG, "Cannot find device owner package", e);
}
if (receivers != null) {
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
for (int i = 0; i < receivers.length; i++) {
if (permission.BIND_DEVICE_ADMIN.equals(receivers[i].permission)) {
@@ -6429,7 +6504,7 @@
}
}
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -6460,12 +6535,12 @@
@Override
public boolean setPermissionGrantState(ComponentName admin, String packageName,
String permission, int grantState) throws RemoteException {
- UserHandle user = Binder.getCallingUserHandle();
+ UserHandle user = mInjector.binderGetCallingUserHandle();
synchronized (this) {
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- final ApplicationInfo ai = AppGlobals.getPackageManager()
+ final ApplicationInfo ai = mIPackageManager
.getApplicationInfo(packageName, 0, user.getIdentifier());
final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
if (targetSdkVersion < android.os.Build.VERSION_CODES.M) {
@@ -6497,7 +6572,7 @@
} catch (SecurityException se) {
return false;
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
@@ -6507,12 +6582,12 @@
String permission) throws RemoteException {
PackageManager packageManager = mContext.getPackageManager();
- UserHandle user = Binder.getCallingUserHandle();
+ UserHandle user = mInjector.binderGetCallingUserHandle();
synchronized (this) {
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- long ident = Binder.clearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
- int granted = AppGlobals.getPackageManager().checkPermission(permission,
+ int granted = mIPackageManager.checkPermission(permission,
packageName, user.getIdentifier());
int permFlags = packageManager.getPermissionFlags(permission, packageName, user);
if ((permFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED)
@@ -6526,8 +6601,17 @@
: DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
}
} finally {
- Binder.restoreCallingIdentity(ident);
+ mInjector.binderRestoreCallingIdentity(ident);
}
}
}
+
+ boolean isPackageInstalledForUser(String packageName, int userHandle) {
+ try {
+ PackageInfo pi = mIPackageManager.getPackageInfo(packageName, 0, userHandle);
+ return (pi != null) && (pi.applicationInfo.flags != 0);
+ } catch (RemoteException re) {
+ throw new RuntimeException("Package manager has died", re);
+ }
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 87cf28f..370cf48 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -101,7 +101,7 @@
public Owners(Context context) {
mContext = context;
- mUserManager = UserManager.get(mContext);
+ mUserManager = context.getSystemService(UserManager.class);
}
/**
@@ -230,20 +230,6 @@
return mDeviceOwner != null;
}
- static boolean isInstalledForUser(String packageName, int userHandle) {
- try {
- PackageInfo pi = (AppGlobals.getPackageManager())
- .getPackageInfo(packageName, 0, userHandle);
- if (pi != null && pi.applicationInfo.flags != 0) {
- return true;
- }
- } catch (RemoteException re) {
- throw new RuntimeException("Package manager has died", re);
- }
-
- return false;
- }
-
private boolean readLegacyOwnerFile(File file) {
if (!file.exists()) {
// Already migrated or the device has no owners.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index aafaf83..1ec1a46 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -867,6 +867,11 @@
if (!disableNonCoreServices) {
mSystemServiceManager.startService(DockObserver.class);
+
+ if (context.getPackageManager().hasSystemFeature
+ (PackageManager.FEATURE_WATCH)) {
+ mSystemServiceManager.startService(ThermalObserver.class);
+ }
}
traceBeginAndSlog("StartWiredAccessoryManager");
@@ -1327,4 +1332,4 @@
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
Slog.i(TAG, name);
}
-}
+}
\ No newline at end of file
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index f6de12d..c6d5a7e 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -588,6 +588,8 @@
Client client = getClient(token);
if (client == null) return;
client.addListener(listener);
+ // Let listener know whether any ports are already busy.
+ updateStickyDeviceStatus(client.mUid, listener);
}
@Override
@@ -597,6 +599,25 @@
client.removeListener(listener);
}
+ // Inform listener of the status of all known devices.
+ private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) {
+ synchronized (mDevicesByInfo) {
+ for (Device device : mDevicesByInfo.values()) {
+ // ignore private devices that our client cannot access
+ if (device.isUidAllowed(uid)) {
+ try {
+ MidiDeviceStatus status = device.getDeviceStatus();
+ if (status != null) {
+ listener.onDeviceStatusChanged(status);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception", e);
+ }
+ }
+ }
+ }
+ }
+
private static final MidiDeviceInfo[] EMPTY_DEVICE_INFO_ARRAY = new MidiDeviceInfo[0];
public MidiDeviceInfo[] getDevices() {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 9ee9cf4..e0d2ac1 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -244,8 +244,9 @@
private PendingIntent createStateMachineCommandIntent(final String cmdName, final int cmd) {
String action = DhcpClient.class.getName() + "." + mIfaceName + "." + cmdName;
- Intent intent = new Intent(action, null)
- .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ Intent intent = new Intent(action, null).addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
// TODO: The intent's package covers the whole of the system server, so it's pretty generic.
// Consider adding some sort of token as well.
intent.setPackage(mContext.getPackageName());
diff --git a/core/java/android/net/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
similarity index 77%
rename from core/java/android/net/IpReachabilityMonitor.java
rename to services/net/java/android/net/ip/IpReachabilityMonitor.java
index 88fb014..982bae0 100644
--- a/core/java/android/net/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package android.net;
+package android.net.ip;
import com.android.internal.annotations.GuardedBy;
+import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LinkProperties.ProvisioningChange;
@@ -31,6 +32,7 @@
import android.net.netlink.StructNdaCacheInfo;
import android.net.netlink.StructNdMsg;
import android.net.netlink.StructNlMsgHdr;
+import android.os.PowerManager;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.NetlinkSocketAddress;
@@ -58,6 +60,74 @@
* Monitors on-link IP reachability and notifies callers whenever any on-link
* addresses of interest appear to have become unresponsive.
*
+ * This code does not concern itself with "why" a neighbour might have become
+ * unreachable. Instead, it primarily reacts to the kernel's notion of IP
+ * reachability for each of the neighbours we know to be critically important
+ * to normal network connectivity. As such, it is often "just the messenger":
+ * the neighbours about which it warns are already deemed by the kernel to have
+ * become unreachable.
+ *
+ *
+ * How it works:
+ *
+ * 1. The "on-link neighbours of interest" found in a given LinkProperties
+ * instance are added to a "watch list" via #updateLinkProperties().
+ * This usually means all default gateways and any on-link DNS servers.
+ *
+ * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH,
+ * RTM_DELNEIGH), watching only for neighbours in the watch list.
+ *
+ * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and
+ * even NUD_PROBE is perfectly normal; we merely record the new state.
+ *
+ * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due
+ * to garbage collection. This is not necessarily of immediate
+ * concern; we record the neighbour as moving to NUD_NONE.
+ *
+ * - A neighbour transitioning to NUD_FAILED (for any reason) is
+ * critically important and is handled as described below in #4.
+ *
+ * 3. All on-link neighbours in the watch list can be forcibly "probed" by
+ * calling #probeAll(). This should be called whenever it is important to
+ * verify that critical neighbours on the link are still reachable, e.g.
+ * when roaming between BSSIDs.
+ *
+ * - The kernel will send unicast ARP requests for IPv4 neighbours and
+ * unicast NS packets for IPv6 neighbours. The expected replies will
+ * likely be unicast.
+ *
+ * - The forced probing is done holding a wakelock. The kernel may,
+ * however, initiate probing of a neighbor on its own, i.e. whenever
+ * a neighbour has expired from NUD_DELAY.
+ *
+ * - The kernel sends:
+ *
+ * /proc/sys/net/ipv{4,6}/neigh/<ifname>/ucast_solicit
+ *
+ * number of probes (usually 3) every:
+ *
+ * /proc/sys/net/ipv{4,6}/neigh/<ifname>/retrans_time_ms
+ *
+ * number of milliseconds (usually 1000ms). This normally results in
+ * 3 unicast packets, 1 per second.
+ *
+ * - If no response is received to any of the probe packets, the kernel
+ * marks the neighbour as being in state NUD_FAILED, and the listening
+ * process in #2 will learn of it.
+ *
+ * 4. We call the supplied Callback#notifyLost() function if the loss of a
+ * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to
+ * become incomplete (a loss of provisioning).
+ *
+ * - For example, losing all our IPv4 on-link DNS servers (or losing
+ * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6)
+ * provisioning; Callback#notifyLost() would be called.
+ *
+ * - Since it can be non-trivial to reacquire certain IP provisioning
+ * state it may be best for the link to disconnect completely and
+ * reconnect afresh.
+ *
+ *
* @hide
*/
public class IpReachabilityMonitor {
@@ -74,6 +144,7 @@
}
private final Object mLock = new Object();
+ private final PowerManager.WakeLock mWakeLock;
private final String mInterfaceName;
private final int mInterfaceIndex;
private final Callback mCallback;
@@ -136,7 +207,8 @@
return returnValue;
}
- public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException {
+ public IpReachabilityMonitor(Context context, String ifName, Callback callback)
+ throws IllegalArgumentException {
mInterfaceName = ifName;
int ifIndex = -1;
try {
@@ -145,6 +217,8 @@
} catch (SocketException | NullPointerException e) {
throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e);
}
+ mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG + "." + mInterfaceName);
mCallback = callback;
mNetlinkSocketObserver = new NetlinkSocketObserver();
mObserverThread = new Thread(mNetlinkSocketObserver);
@@ -291,6 +365,17 @@
synchronized (mLock) {
ipProbeList.addAll(mIpWatchList.keySet());
}
+
+ if (!ipProbeList.isEmpty() && stillRunning()) {
+ // Keep the CPU awake long enough to allow all ARP/ND
+ // probes a reasonable chance at success. See b/23197666.
+ //
+ // The wakelock we use is (by default) refcounted, and this version
+ // of acquire(timeout) queues a release message to keep acquisitions
+ // and releases balanced.
+ mWakeLock.acquire(getProbeWakeLockDuration());
+ }
+
for (InetAddress target : ipProbeList) {
if (!stillRunning()) {
break;
@@ -299,6 +384,22 @@
}
}
+ private long getProbeWakeLockDuration() {
+ // Ideally, this would be computed by examining the values of:
+ //
+ // /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit
+ //
+ // and:
+ //
+ // /proc/sys/net/ipv[46]/neigh/<ifname>/retrans_time_ms
+ //
+ // For now, just make some assumptions.
+ final long numUnicastProbes = 3;
+ final long retransTimeMs = 1000;
+ final long gracePeriodMs = 500;
+ return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
+ }
+
// TODO: simply the number of objects by making this extend Thread.
private final class NetlinkSocketObserver implements Runnable {
diff --git a/core/java/android/net/netlink/NetlinkConstants.java b/services/net/java/android/net/netlink/NetlinkConstants.java
similarity index 100%
rename from core/java/android/net/netlink/NetlinkConstants.java
rename to services/net/java/android/net/netlink/NetlinkConstants.java
diff --git a/core/java/android/net/netlink/NetlinkErrorMessage.java b/services/net/java/android/net/netlink/NetlinkErrorMessage.java
similarity index 100%
rename from core/java/android/net/netlink/NetlinkErrorMessage.java
rename to services/net/java/android/net/netlink/NetlinkErrorMessage.java
diff --git a/core/java/android/net/netlink/NetlinkMessage.java b/services/net/java/android/net/netlink/NetlinkMessage.java
similarity index 100%
rename from core/java/android/net/netlink/NetlinkMessage.java
rename to services/net/java/android/net/netlink/NetlinkMessage.java
diff --git a/core/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java
similarity index 100%
rename from core/java/android/net/netlink/NetlinkSocket.java
rename to services/net/java/android/net/netlink/NetlinkSocket.java
diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/services/net/java/android/net/netlink/RtNetlinkNeighborMessage.java
similarity index 100%
rename from core/java/android/net/netlink/RtNetlinkNeighborMessage.java
rename to services/net/java/android/net/netlink/RtNetlinkNeighborMessage.java
diff --git a/core/java/android/net/netlink/StructNdMsg.java b/services/net/java/android/net/netlink/StructNdMsg.java
similarity index 100%
rename from core/java/android/net/netlink/StructNdMsg.java
rename to services/net/java/android/net/netlink/StructNdMsg.java
diff --git a/core/java/android/net/netlink/StructNdaCacheInfo.java b/services/net/java/android/net/netlink/StructNdaCacheInfo.java
similarity index 100%
rename from core/java/android/net/netlink/StructNdaCacheInfo.java
rename to services/net/java/android/net/netlink/StructNdaCacheInfo.java
diff --git a/core/java/android/net/netlink/StructNlAttr.java b/services/net/java/android/net/netlink/StructNlAttr.java
similarity index 100%
rename from core/java/android/net/netlink/StructNlAttr.java
rename to services/net/java/android/net/netlink/StructNlAttr.java
diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/services/net/java/android/net/netlink/StructNlMsgErr.java
similarity index 100%
rename from core/java/android/net/netlink/StructNlMsgErr.java
rename to services/net/java/android/net/netlink/StructNlMsgErr.java
diff --git a/core/java/android/net/netlink/StructNlMsgHdr.java b/services/net/java/android/net/netlink/StructNlMsgHdr.java
similarity index 100%
rename from core/java/android/net/netlink/StructNlMsgHdr.java
rename to services/net/java/android/net/netlink/StructNlMsgHdr.java
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 7d1282b..c147bcc 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -67,6 +67,33 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin_sample" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin2"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin_sample" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin3"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin_sample" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
</application>
<instrumentation
diff --git a/core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java b/services/tests/servicestests/src/android/net/netlink/NetlinkErrorMessageTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java
rename to services/tests/servicestests/src/android/net/netlink/NetlinkErrorMessageTest.java
diff --git a/core/tests/coretests/src/android/net/netlink/NetlinkSocketTest.java b/services/tests/servicestests/src/android/net/netlink/NetlinkSocketTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/netlink/NetlinkSocketTest.java
rename to services/tests/servicestests/src/android/net/netlink/NetlinkSocketTest.java
diff --git a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java b/services/tests/servicestests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
rename to services/tests/servicestests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
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 0f20dde..f9aa124 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -82,7 +82,7 @@
mAms.addAccountExplicitly(a31, "p31", null);
mAms.addAccountExplicitly(a32, "p32", null);
- Account[] accounts = mAms.getAccounts(null);
+ Account[] accounts = mAms.getAccounts(null, mContext.getOpPackageName());
Arrays.sort(accounts, new AccountSorter());
assertEquals(6, accounts.length);
assertEquals(a11, accounts[0]);
@@ -92,7 +92,7 @@
assertEquals(a22, accounts[4]);
assertEquals(a32, accounts[5]);
- accounts = mAms.getAccounts("type1" );
+ accounts = mAms.getAccounts("type1", mContext.getOpPackageName());
Arrays.sort(accounts, new AccountSorter());
assertEquals(3, accounts.length);
assertEquals(a11, accounts[0]);
@@ -101,7 +101,7 @@
mAms.removeAccountInternal(a21);
- accounts = mAms.getAccounts("type1" );
+ accounts = mAms.getAccounts("type1", mContext.getOpPackageName());
Arrays.sort(accounts, new AccountSorter());
assertEquals(2, accounts.length);
assertEquals(a11, accounts[0]);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
deleted file mode 100644
index ca270e7..0000000
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-import android.app.admin.DeviceAdminReceiver;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
-/**
- * Tests for application restrictions persisting via profile owner:
- * make -j FrameworksServicesTests
- * runtest --path frameworks/base/services/tests/servicestests/ \
- * src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
- */
-public class ApplicationRestrictionsTest extends AndroidTestCase {
-
- static DevicePolicyManager sDpm;
- static ComponentName sAdminReceiver;
- private static final String RESTRICTED_APP = "com.example.restrictedApp";
- static boolean sAddBack = false;
-
- public static class AdminReceiver extends DeviceAdminReceiver {
-
- @Override
- public void onDisabled(Context context, Intent intent) {
- if (sAddBack) {
- sDpm.setActiveAdmin(sAdminReceiver, false);
- sAddBack = false;
- }
-
- super.onDisabled(context, intent);
- }
- }
-
- @Override
- public void setUp() {
- final Context context = getContext();
- sAdminReceiver = new ComponentName(mContext.getPackageName(),
- AdminReceiver.class.getName());
- sDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
- Settings.Secure.putInt(context.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0);
- sDpm.setProfileOwner(sAdminReceiver, "Test", UserHandle.myUserId());
- Settings.Secure.putInt(context.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 1);
- // Remove the admin if already registered. It's async, so add it back
- // when the admin gets a broadcast. Otherwise add it back right away.
- if (sDpm.isAdminActive(sAdminReceiver)) {
- sAddBack = true;
- sDpm.removeActiveAdmin(sAdminReceiver);
- } else {
- sDpm.setActiveAdmin(sAdminReceiver, false);
- }
- }
-
- @Override
- public void tearDown() {
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0);
- sDpm.removeActiveAdmin(sAdminReceiver);
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 1);
- }
-
- public void testSettingRestrictions() {
- Bundle restrictions = new Bundle();
- restrictions.putString("KEY_STRING", "Foo");
- assertNotNull(sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP));
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
- Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
- assertNotNull(returned);
- assertEquals(returned.size(), 1);
- assertEquals(returned.get("KEY_STRING"), "Foo");
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
- returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
- assertEquals(returned.size(), 0);
- }
-
- public void testRestrictionTypes() {
- Bundle restrictions = new Bundle();
- restrictions.putString("KEY_STRING", "Foo");
- restrictions.putInt("KEY_INT", 7);
- restrictions.putBoolean("KEY_BOOLEAN", true);
- restrictions.putBoolean("KEY_BOOLEAN_2", false);
- restrictions.putString("KEY_STRING_2", "Bar");
- restrictions.putStringArray("KEY_STR_ARRAY", new String[] { "Foo", "Bar" });
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
- Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
- assertTrue(returned.getBoolean("KEY_BOOLEAN"));
- assertFalse(returned.getBoolean("KEY_BOOLEAN_2"));
- assertFalse(returned.getBoolean("KEY_BOOLEAN_3"));
- assertEquals(returned.getInt("KEY_INT"), 7);
- assertTrue(returned.get("KEY_BOOLEAN") instanceof Boolean);
- assertTrue(returned.get("KEY_INT") instanceof Integer);
- assertEquals(returned.get("KEY_STRING"), "Foo");
- assertEquals(returned.get("KEY_STRING_2"), "Bar");
- assertTrue(returned.getStringArray("KEY_STR_ARRAY") instanceof String[]);
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
- }
-
- public void testTextEscaping() {
- String fancyText = "<This contains XML/> <JSON> "
- + "{ \"One\": { \"OneOne\": \"11\", \"OneTwo\": \"12\" }, \"Two\": \"2\" } <JSON/>";
- Bundle restrictions = new Bundle();
- restrictions.putString("KEY_FANCY_TEXT", fancyText);
- sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
- Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
- assertEquals(returned.getString("KEY_FANCY_TEXT"), fancyText);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
new file mode 100644
index 0000000..c6c7497
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import android.app.IActivityManager;
+import android.app.NotificationManager;
+import android.app.backup.IBackupManager;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.media.IAudioService;
+import android.os.Looper;
+import android.os.PowerManagerInternal;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.view.IWindowManager;
+
+import java.io.File;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+/**
+ * Overrides {@link #DevicePolicyManagerService} for dependency injection.
+ */
+public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerService {
+ /**
+ * Overrides {@link #Owners} for dependency injection.
+ */
+ public static class OwnersTestable extends Owners {
+ public static final String LEGACY_FILE = "legacy.xml";
+ public static final String DEVICE_OWNER_FILE = "device_owner2.xml";
+ public static final String PROFILE_OWNER_FILE_BASE = "profile_owner.xml";
+
+ private final File mLegacyFile;
+ private final File mDeviceOwnerFile;
+ private final File mProfileOwnerBase;
+
+ public OwnersTestable(Context context, File dataDir) {
+ super(context);
+ mLegacyFile = new File(dataDir, LEGACY_FILE);
+ mDeviceOwnerFile = new File(dataDir, DEVICE_OWNER_FILE);
+ mProfileOwnerBase = new File(dataDir, PROFILE_OWNER_FILE_BASE);
+ }
+
+ @Override
+ File getLegacyConfigFileWithTestOverride() {
+ return mLegacyFile;
+ }
+
+ @Override
+ File getDeviceOwnerFileWithTestOverride() {
+ return mDeviceOwnerFile;
+ }
+
+ @Override
+ File getProfileOwnerFileWithTestOverride(int userId) {
+ return new File(mDeviceOwnerFile.getAbsoluteFile() + "-" + userId);
+ }
+ }
+
+ public final DpmMockContext context;
+
+ public DevicePolicyManagerServiceTestable(DpmMockContext context, File dataDir) {
+ this(new MockInjector(context, dataDir));
+ }
+
+ private DevicePolicyManagerServiceTestable(MockInjector injector) {
+ super(injector);
+ this.context = injector.context;
+ }
+
+ private static class MockInjector extends Injector {
+
+ public final DpmMockContext context;
+
+ public final File dataDir;
+
+ public final File systemUserDataDir;
+ public final File secondUserDataDir;
+
+ private MockInjector(DpmMockContext context, File dataDir) {
+ super(context);
+ this.context = context;
+ this.dataDir = dataDir;
+
+ systemUserDataDir = new File(dataDir, "user0");
+ DpmTestUtils.clearDir(dataDir);
+
+ secondUserDataDir = new File(dataDir, "user" + DpmMockContext.CALLER_USER_HANDLE);
+ DpmTestUtils.clearDir(secondUserDataDir);
+
+ when(context.environment.getUserSystemDirectory(
+ eq(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(secondUserDataDir);
+ }
+
+ @Override
+ Owners newOwners() {
+ return new OwnersTestable(context, dataDir);
+ }
+
+ @Override
+ UserManager getUserManager() {
+ return context.userManager;
+ }
+
+ @Override
+ PowerManagerInternal getPowerManagerInternal() {
+ return context.powerManagerInternal;
+ }
+
+ @Override
+ NotificationManager getNotificationManager() {
+ return context.notificationManager;
+ }
+
+ @Override
+ IWindowManager getIWindowManager() {
+ return context.iwindowManager;
+ }
+
+ @Override
+ IActivityManager getIActivityManager() {
+ return context.iactivityManager;
+ }
+
+ @Override
+ IPackageManager getIPackageManager() {
+ return context.ipackageManager;
+ }
+
+ @Override
+ IBackupManager getIBackupManager() {
+ return context.ibackupManager;
+ }
+
+ @Override
+ IAudioService getIAudioService() {
+ return context.iaudioService;
+ }
+
+ @Override
+ Looper getMyLooper() {
+ return Looper.getMainLooper();
+ }
+
+ @Override
+ LockPatternUtils newLockPatternUtils() {
+ return context.lockPatternUtils;
+ }
+
+ @Override
+ String getDevicePolicyFilePathForSystemUser() {
+ return systemUserDataDir.getAbsolutePath();
+ }
+
+ @Override
+ long binderClearCallingIdentity() {
+ return context.binder.clearCallingIdentity();
+ }
+
+ @Override
+ void binderRestoreCallingIdentity(long token) {
+ context.binder.restoreCallingIdentity(token);
+ }
+
+ @Override
+ int binderGetCallingUid() {
+ return context.binder.getCallingUid();
+ }
+
+ @Override
+ int binderGetCallingPid() {
+ return context.binder.getCallingPid();
+ }
+
+ @Override
+ UserHandle binderGetCallingUserHandle() {
+ return context.binder.getCallingUserHandle();
+ }
+
+ @Override
+ boolean binderIsCallingUidMyUid() {
+ return context.binder.isCallerUidMyUid();
+ }
+
+ @Override
+ File environmentGetUserSystemDirectory(int userId) {
+ return context.environment.getUserSystemDirectory(userId);
+ }
+
+ @Override
+ void powerManagerGoToSleep(long time, int reason, int flags) {
+ context.powerManager.goToSleep(time, reason, flags);
+ }
+
+ @Override
+ boolean systemPropertiesGetBoolean(String key, boolean def) {
+ return context.systemProperties.getBoolean(key, def);
+ }
+
+ @Override
+ long systemPropertiesGetLong(String key, long def) {
+ return context.systemProperties.getLong(key, def);
+ }
+
+ @Override
+ String systemPropertiesGet(String key, String def) {
+ return context.systemProperties.get(key, def);
+ }
+
+ @Override
+ String systemPropertiesGet(String key) {
+ return context.systemProperties.get(key);
+ }
+
+ @Override
+ void systemPropertiesSet(String key, String value) {
+ context.systemProperties.set(key, value);
+ }
+
+ @Override
+ boolean userManagerIsSplitSystemUser() {
+ return context.userManagerForMock.isSplitSystemUser();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
new file mode 100644
index 0000000..0072f52
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import com.android.server.LocalServices;
+
+import android.Manifest.permission;
+import android.app.Activity;
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.content.pm.PackageInfo;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.util.Pair;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for DevicePolicyManager( and DevicePolicyManagerService).
+ *
+ m FrameworksServicesTests &&
+ adb install \
+ -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+
+ (mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
+ */
+public class DevicePolicyManagerTest extends DpmTestBase {
+
+
+ private DpmMockContext mContext;
+ public DevicePolicyManager dpm;
+ public DevicePolicyManagerServiceTestable dpms;
+ public ComponentName admin1;
+ public ComponentName admin2;
+ public ComponentName admin3;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mContext = getContext();
+
+ when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
+ .thenReturn(true);
+
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ dpms = new DevicePolicyManagerServiceTestable(mContext, dataDir);
+ dpm = new DevicePolicyManagerTestable(mContext, dpms);
+
+ admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
+ admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
+ admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
+
+ setUpPackageManagerForAdmin(admin1);
+ setUpPackageManagerForAdmin(admin2);
+ setUpPackageManagerForAdmin(admin3);
+
+ setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+ setUpPackageInfo();
+ setUpUserManager();
+ }
+
+ /**
+ * Set up a mock result for {@link PackageManager#queryBroadcastReceivers}. We'll return
+ * the actual ResolveInfo for the admin component, but we need to mock PM so it'll return
+ * it for user {@link DpmMockContext#CALLER_USER_HANDLE}.
+ */
+ private void setUpPackageManagerForAdmin(ComponentName admin) {
+ final Intent resolveIntent = new Intent();
+ resolveIntent.setComponent(admin);
+ final List<ResolveInfo> realResolveInfo =
+ mRealTestContext.getPackageManager().queryBroadcastReceivers(
+ resolveIntent,
+ PackageManager.GET_META_DATA);
+ assertNotNull(realResolveInfo);
+ assertEquals(1, realResolveInfo.size());
+
+ // We need to rewrite the UID in the activity info.
+ realResolveInfo.get(0).activityInfo.applicationInfo.uid = DpmMockContext.CALLER_UID;
+
+ doReturn(realResolveInfo).when(mContext.packageManager).queryBroadcastReceivers(
+ MockUtils.checkIntentComponent(admin),
+ eq(PackageManager.GET_META_DATA
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
+ eq(DpmMockContext.CALLER_USER_HANDLE)
+ );
+ }
+
+ /**
+ * Set up a mock result for {@link IPackageManager#getApplicationInfo} for user
+ * {@link DpmMockContext#CALLER_USER_HANDLE}.
+ */
+ private void setUpApplicationInfo(int enabledSetting) throws Exception {
+ final ApplicationInfo ai = mRealTestContext.getPackageManager().getApplicationInfo(
+ admin1.getPackageName(),
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+
+ ai.enabledSetting = enabledSetting;
+
+ doReturn(ai).when(mContext.ipackageManager).getApplicationInfo(
+ eq(admin1.getPackageName()),
+ eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ /**
+ * Set up a mock result for {@link IPackageManager#getPackageInfo(String, int, int)} for user
+ * {@link DpmMockContext#CALLER_USER_HANDLE} as well as the system user.
+ */
+ private void setUpPackageInfo() throws Exception {
+ final PackageInfo pi = mRealTestContext.getPackageManager().getPackageInfo(
+ admin1.getPackageName(), 0);
+ assertTrue(pi.applicationInfo.flags != 0);
+
+ doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+ eq(admin1.getPackageName()),
+ eq(0),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+ doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+ eq(admin1.getPackageName()),
+ eq(0),
+ eq(UserHandle.USER_SYSTEM));
+ }
+
+ private void setUpUserManager() {
+ // Emulate UserManager.set/getApplicationRestriction().
+ final Map<Pair<String, UserHandle>, Bundle> appRestrictions = new HashMap<>();
+
+ // UM.setApplicationRestrictions() will save to appRestrictions.
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ String pkg = (String) invocation.getArguments()[0];
+ Bundle bundle = (Bundle) invocation.getArguments()[1];
+ UserHandle user = (UserHandle) invocation.getArguments()[2];
+
+ appRestrictions.put(Pair.create(pkg, user), bundle);
+
+ return null;
+ }
+ }).when(mContext.userManager).setApplicationRestrictions(
+ anyString(), any(Bundle.class), any(UserHandle.class));
+
+ // UM.getApplicationRestrictions() will read from appRestrictions.
+ doAnswer(new Answer<Bundle>() {
+ @Override
+ public Bundle answer(InvocationOnMock invocation) throws Throwable {
+ String pkg = (String) invocation.getArguments()[0];
+ UserHandle user = (UserHandle) invocation.getArguments()[1];
+
+ return appRestrictions.get(Pair.create(pkg, user));
+ }
+ }).when(mContext.userManager).getApplicationRestrictions(
+ anyString(), any(UserHandle.class));
+
+ // System user is always running.
+ when(mContext.userManager.isUserRunning(MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)))
+ .thenReturn(true);
+
+ // Set up (default) UserInfo for CALLER_USER_HANDLE.
+ final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE,
+ "user" + DpmMockContext.CALLER_USER_HANDLE, 0);
+
+ when(mContext.userManager.getUserInfo(eq(DpmMockContext.CALLER_USER_HANDLE)))
+ .thenReturn(uh);
+ }
+
+ private void setAsProfileOwner(ComponentName admin) {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE, "user", 0);
+
+ // DO needs to be an DA.
+ dpm.setActiveAdmin(admin, /* replace =*/ false);
+
+ // Fire!
+ assertTrue(dpm.setProfileOwner(admin, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
+
+ // Check
+ assertEquals(admin1, dpm.getProfileOwnerAsUser(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ public void testHasNoFeature() throws Exception {
+ when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
+ .thenReturn(false);
+
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ new DevicePolicyManagerServiceTestable(mContext, dataDir);
+
+ // If the device has no DPMS feature, it shouldn't register the local service.
+ assertNull(LocalServices.getService(DevicePolicyManagerInternal.class));
+ }
+
+ /**
+ * Caller doesn't have proper permissions.
+ */
+ public void testSetActiveAdmin_SecurityException() {
+ // 1. Failure cases.
+
+ // Caller doesn't have MANAGE_DEVICE_ADMINS.
+ try {
+ dpm.setActiveAdmin(admin1, false);
+ fail("Didn't throw SecurityException");
+ } catch (SecurityException expected) {
+ }
+
+ // Caller has MANAGE_DEVICE_ADMINS, but for different user.
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+ try {
+ dpm.setActiveAdmin(admin1, false, DpmMockContext.CALLER_USER_HANDLE + 1);
+ fail("Didn't throw SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Test for:
+ * {@link DevicePolicyManager#setActiveAdmin}
+ * with replace=false and replace=true
+ * {@link DevicePolicyManager#isAdminActive}
+ * {@link DevicePolicyManager#isAdminActiveAsUser}
+ * {@link DevicePolicyManager#getActiveAdmins}
+ * {@link DevicePolicyManager#getActiveAdminsAsUser}
+ */
+ public void testSetActiveAdmin() throws Exception {
+ // 1. Make sure the caller has proper permissions.
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ // 2. Call the API.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ // 3. Verify internal calls.
+
+ // Check if the boradcast is sent.
+ verify(mContext.spiedContext).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ verify(mContext.spiedContext).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+
+ verify(mContext.ipackageManager, times(1)).setApplicationEnabledSetting(
+ eq(admin1.getPackageName()),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT),
+ eq(PackageManager.DONT_KILL_APP),
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ anyString());
+
+ // TODO Verify other calls too.
+
+ // Make sure it's active admin1.
+ assertTrue(dpm.isAdminActive(admin1));
+ assertFalse(dpm.isAdminActive(admin2));
+ assertFalse(dpm.isAdminActive(admin3));
+
+ // But not admin1 for a different user.
+
+ // For this to work, caller needs android.permission.INTERACT_ACROSS_USERS_FULL.
+ // (Because we're checking a different user's status from CALLER_USER_HANDLE.)
+ mContext.callerPermissions.add("android.permission.INTERACT_ACROSS_USERS_FULL");
+
+ assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE + 1));
+ assertFalse(dpm.isAdminActiveAsUser(admin2, DpmMockContext.CALLER_USER_HANDLE + 1));
+
+ mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL");
+
+ // Next, add one more admin.
+ // Before doing so, update the application info, now it's enabled.
+ setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+
+ dpm.setActiveAdmin(admin2, /* replace =*/ false);
+
+ // Now we have two admins.
+ assertTrue(dpm.isAdminActive(admin1));
+ assertTrue(dpm.isAdminActive(admin2));
+ assertFalse(dpm.isAdminActive(admin3));
+
+ // Admin2 was already enabled, so setApplicationEnabledSetting() shouldn't have called
+ // again. (times(1) because it was previously called for admin1)
+ verify(mContext.ipackageManager, times(1)).setApplicationEnabledSetting(
+ eq(admin1.getPackageName()),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT),
+ eq(PackageManager.DONT_KILL_APP),
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ anyString());
+
+ // 4. Add the same admin1 again without replace, which should throw.
+ try {
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ fail("Didn't throw");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // 5. Add the same admin1 again with replace, which should succeed.
+ dpm.setActiveAdmin(admin1, /* replace =*/ true);
+
+ // TODO make sure it's replaced.
+
+ // 6. Test getActiveAdmins()
+ List<ComponentName> admins = dpm.getActiveAdmins();
+ assertEquals(2, admins.size());
+ assertEquals(admin1, admins.get(0));
+ assertEquals(admin2, admins.get(1));
+
+ // Another user has no admins.
+ mContext.callerPermissions.add("android.permission.INTERACT_ACROSS_USERS_FULL");
+
+ assertEquals(0, DpmTestUtils.getListSizeAllowingNull(
+ dpm.getActiveAdminsAsUser(DpmMockContext.CALLER_USER_HANDLE + 1)));
+
+ mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL");
+ }
+
+ /**
+ * Test for:
+ * {@link DevicePolicyManager#setActiveAdmin}
+ * with replace=false
+ */
+ public void testSetActiveAdmin_twiceWithoutReplace() throws Exception {
+ // 1. Make sure the caller has proper permissions.
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ assertTrue(dpm.isAdminActive(admin1));
+
+ // Add the same admin1 again without replace, which should throw.
+ try {
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ fail("Didn't throw");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ /**
+ * Test for:
+ * {@link DevicePolicyManager#removeActiveAdmin}
+ */
+ public void testRemoveActiveAdmin_SecurityException() {
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ // Add admin.
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ assertTrue(dpm.isAdminActive(admin1));
+
+ assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+ // Directly call the DPMS method with a different userid, which should fail.
+ try {
+ dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE + 1);
+ fail("Didn't throw SecurityException");
+ } catch (SecurityException expected) {
+ }
+
+ // Try to remove active admin with a different caller userid should fail too, without
+ // having MANAGE_DEVICE_ADMINS.
+ mContext.callerPermissions.clear();
+
+ mContext.binder.callingUid = 1234567;
+ try {
+ dpm.removeActiveAdmin(admin1);
+ fail("Didn't throw SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Test for:
+ * {@link DevicePolicyManager#removeActiveAdmin}
+ */
+ public void testRemoveActiveAdmin_fromDifferentUserWithMINTERACT_ACROSS_USERS_FULL() {
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ // Add admin1.
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ assertTrue(dpm.isAdminActive(admin1));
+ assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+ // Different user, but should work, because caller has proper permissions.
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+ mContext.binder.callingUid = 1234567;
+ dpm.removeActiveAdmin(admin1);
+
+ assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+ // TODO DO Still can't be removed in this case.
+ }
+
+ /**
+ * Test for:
+ * {@link DevicePolicyManager#removeActiveAdmin}
+ */
+ public void testRemoveActiveAdmin_sameUserNoMANAGE_DEVICE_ADMINS() {
+ // Need MANAGE_DEVICE_ADMINS for setActiveAdmin. We'll remove it later.
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ // Add admin1.
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ assertTrue(dpm.isAdminActive(admin1));
+ assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+ // Broadcast from saveSettingsLocked().
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+
+ // Remove. No permissions, but same user, so it'll work.
+ mContext.callerPermissions.clear();
+ dpm.removeActiveAdmin(admin1);
+
+ final ArgumentCaptor<BroadcastReceiver> brCap =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+ // Is removing now, but not removed yet.
+ assertTrue(dpm.isAdminActive(admin1));
+ assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+ verify(mContext.spiedContext).sendOrderedBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
+ isNull(String.class),
+ brCap.capture(),
+ eq(dpms.mHandler),
+ eq(Activity.RESULT_OK),
+ isNull(String.class),
+ isNull(Bundle.class));
+
+ brCap.getValue().onReceive(mContext, null);
+
+ assertFalse(dpm.isAdminActive(admin1));
+ assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+ // Again broadcast from saveSettingsLocked().
+ verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+
+ // TODO Check other internal calls.
+ }
+
+ /**
+ * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs
+ * successfully.
+ */
+ public void testSetDeviceOwner() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // Call from a process on the system user.
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // DO needs to be an DA.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ // Fire!
+ assertTrue(dpm.setDeviceOwner(admin1.getPackageName(), "owner-name"));
+
+ // Verify internal calls.
+ verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+ eq(admin1.getPackageName()));
+
+ // TODO We should check if the caller has called clearCallerIdentity().
+ verify(mContext.ibackupManager, times(1)).setBackupServiceActive(
+ eq(UserHandle.USER_SYSTEM), eq(false));
+
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
+ MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+
+ assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+
+ // TODO Test getDeviceOwnerName() too. To do so, we need to change
+ // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
+ }
+
+ /**
+ * Test for: {@link DevicePolicyManager#setDeviceOwner} Package doesn't exist.
+ */
+ public void testSetDeviceOwner_noSuchPackage() {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // Call from a process on the system user.
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // DO needs to be an DA.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ try {
+ dpm.setDeviceOwner("a.b.c");
+ fail("Didn't throw IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testSetDeviceOwner_failures() throws Exception {
+ // TODO Test more failure cases. Basically test all chacks in enforceCanSetDeviceOwner().
+ }
+
+ public void testSetProfileOwner() throws Exception {
+ setAsProfileOwner(admin1);
+ }
+
+ public void testSetProfileOwner_failures() throws Exception {
+ // TODO Test more failure cases. Basically test all chacks in enforceCanSetProfileOwner().
+ }
+
+ public void testSetGetApplicationRestriction() {
+ setAsProfileOwner(admin1);
+
+ {
+ Bundle rest = new Bundle();
+ rest.putString("KEY_STRING", "Foo1");
+ dpm.setApplicationRestrictions(admin1, "pkg1", rest);
+ }
+
+ {
+ Bundle rest = new Bundle();
+ rest.putString("KEY_STRING", "Foo2");
+ dpm.setApplicationRestrictions(admin1, "pkg2", rest);
+ }
+
+ {
+ Bundle returned = dpm.getApplicationRestrictions(admin1, "pkg1");
+ assertNotNull(returned);
+ assertEquals(returned.size(), 1);
+ assertEquals(returned.get("KEY_STRING"), "Foo1");
+ }
+
+ {
+ Bundle returned = dpm.getApplicationRestrictions(admin1, "pkg2");
+ assertNotNull(returned);
+ assertEquals(returned.size(), 1);
+ assertEquals(returned.get("KEY_STRING"), "Foo2");
+ }
+
+ dpm.setApplicationRestrictions(admin1, "pkg2", new Bundle());
+ assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
new file mode 100644
index 0000000..325bf9f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+
+/**
+ * Overrides {@link #DevicePolicyManager} for dependency injection.
+ */
+public class DevicePolicyManagerTestable extends DevicePolicyManager {
+ public final DevicePolicyManagerServiceTestable dpms;
+
+ public DevicePolicyManagerTestable(DpmMockContext context,
+ DevicePolicyManagerServiceTestable dpms) {
+ super(context, dpms);
+ this.dpms = dpms;
+ }
+
+ @Override
+ public int myUserId() {
+ return DpmMockContext.CALLER_USER_HANDLE;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index c2b8981..3b30a37 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -16,31 +16,387 @@
package com.android.server.devicepolicy;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.app.IActivityManager;
+import android.app.NotificationManager;
+import android.app.backup.IBackupManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.media.IAudioService;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PowerManager.WakeLock;
+import android.os.PowerManagerInternal;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.test.mock.MockContext;
+import android.view.IWindowManager;
+
+import org.junit.Assert;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
-public class DpmMockContext extends ContextWrapper {
- private final UserManager mMockUserManager;
+/**
+ * Context used throughout DPMS tests.
+ */
+public class DpmMockContext extends MockContext {
+ /**
+ * User-id of a non-system user we use throughout unit tests.
+ */
+ public static final int CALLER_USER_HANDLE = 20;
+ /**
+ * UID corresponding to {@link #CALLER_USER_HANDLE}.
+ */
+ public static final int CALLER_UID = UserHandle.PER_USER_RANGE * CALLER_USER_HANDLE + 123;
- public DpmMockContext(Context context) {
- super(context);
- mMockUserManager = mock(UserManager.class);
+ /**
+ * UID used when a caller is on the system user.
+ */
+ public static final int CALLER_SYSTEM_USER_UID = 123;
+
+ /**
+ * PID of the caller.
+ */
+ public static final int CALLER_PID = 22222;
+
+ /**
+ * UID of the system server.
+ */
+ public static final int SYSTEM_UID = android.os.Process.SYSTEM_UID;
+
+ /**
+ * PID of the system server.
+ */
+ public static final int SYSTEM_PID = 11111;
+
+ public static class MockBinder {
+ public int callingUid = CALLER_UID;
+ public int callingPid = CALLER_PID;
+
+ public long clearCallingIdentity() {
+ final long token = (((long) callingUid) << 32) | (callingPid);
+ callingUid = SYSTEM_UID;
+ callingPid = SYSTEM_PID;
+ return token;
+ }
+
+ public void restoreCallingIdentity(long token) {
+ callingUid = (int) (token >> 32);
+ callingPid = (int) token;
+ }
+
+ public int getCallingUid() {
+ return callingUid;
+ }
+
+ public int getCallingPid() {
+ return callingPid;
+ }
+
+ public UserHandle getCallingUserHandle() {
+ return new UserHandle(UserHandle.getUserId(getCallingUid()));
+ }
+
+ public boolean isCallerUidMyUid() {
+ return callingUid == SYSTEM_UID;
+ }
}
- public UserManager getMockUserManager() {
- return mMockUserManager;
+ public static class EnvironmentForMock {
+ public File getUserSystemDirectory(int userId) {
+ return null;
+ }
+ }
+
+ public static class PowerManagerForMock {
+ public WakeLock newWakeLock(int levelAndFlags, String tag) {
+ return null;
+ }
+
+ public void goToSleep(long time, int reason, int flags) {
+ }
+ }
+
+ public static class SystemPropertiesForMock {
+ public boolean getBoolean(String key, boolean def) {
+ return false;
+ }
+
+ public long getLong(String key, long def) {
+ return 0;
+ }
+
+ public String get(String key, String def) {
+ return null;
+ }
+
+ public String get(String key) {
+ return null;
+ }
+
+ public void set(String key, String value) {
+ }
+ }
+
+ public static class UserManagerForMock {
+ public boolean isSplitSystemUser() {
+ return false;
+ }
+ }
+
+ public final Context realTestContext;
+
+ /**
+ * Use this instance to verify unimplemented methods such as {@link #sendBroadcast}.
+ * (Spying on {@code this} instance will confuse mockito somehow and I got weired "wrong number
+ * of arguments" exceptions.)
+ */
+ public final Context spiedContext;
+
+ public final MockBinder binder;
+ public final EnvironmentForMock environment;
+ public final SystemPropertiesForMock systemProperties;
+ public final UserManager userManager;
+ public final UserManagerForMock userManagerForMock;
+ public final PowerManagerForMock powerManager;
+ public final PowerManagerInternal powerManagerInternal;
+ public final NotificationManager notificationManager;
+ public final IWindowManager iwindowManager;
+ public final IActivityManager iactivityManager;
+ public final IPackageManager ipackageManager;
+ public final IBackupManager ibackupManager;
+ public final IAudioService iaudioService;
+ public final LockPatternUtils lockPatternUtils;
+
+ /** Note this is a partial mock, not a real mock. */
+ public final PackageManager packageManager;
+
+ public final List<String> callerPermissions = new ArrayList<>();
+
+ public DpmMockContext(Context context) {
+ realTestContext = context;
+ binder = new MockBinder();
+ environment = mock(EnvironmentForMock.class);
+ systemProperties= mock(SystemPropertiesForMock.class);
+ userManager = mock(UserManager.class);
+ userManagerForMock = mock(UserManagerForMock.class);
+ powerManager = mock(PowerManagerForMock.class);
+ powerManagerInternal = mock(PowerManagerInternal.class);
+ notificationManager = mock(NotificationManager.class);
+ iwindowManager = mock(IWindowManager.class);
+ iactivityManager = mock(IActivityManager.class);
+ ipackageManager = mock(IPackageManager.class);
+ ibackupManager = mock(IBackupManager.class);
+ iaudioService = mock(IAudioService.class);
+ lockPatternUtils = mock(LockPatternUtils.class);
+
+ // Package manager is huge, so we use a partial mock instead.
+ packageManager = spy(context.getPackageManager());
+
+ spiedContext = mock(Context.class);
}
@Override
public Object getSystemService(String name) {
switch (name) {
case Context.USER_SERVICE:
- return mMockUserManager;
+ return userManager;
+ case Context.POWER_SERVICE:
+ return powerManager;
}
- return super.getSystemService(name);
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ return realTestContext.getSystemServiceName(serviceClass);
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return packageManager;
+ }
+
+ @Override
+ public void enforceCallingOrSelfPermission(String permission, String message) {
+ if (binder.getCallingUid() == SYSTEM_UID) {
+ return; // Assume system has all permissions.
+ }
+ if (!callerPermissions.contains(permission)) {
+ throw new SecurityException("Caller doesn't have " + permission + " : " + message);
+ }
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent) {
+ spiedContext.sendBroadcast(intent);
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent, String receiverPermission) {
+ spiedContext.sendBroadcast(intent, receiverPermission);
+ }
+
+ @Override
+ public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) {
+ spiedContext.sendBroadcastMultiplePermissions(intent, receiverPermissions);
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
+ spiedContext.sendBroadcast(intent, receiverPermission, options);
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+ spiedContext.sendBroadcast(intent, receiverPermission, appOp);
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
+ spiedContext.sendOrderedBroadcast(intent, receiverPermission);
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ spiedContext.sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler,
+ initialCode, initialData, initialExtras);
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission, Bundle options,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ spiedContext.sendOrderedBroadcast(intent, receiverPermission, options, resultReceiver,
+ scheduler,
+ initialCode, initialData, initialExtras);
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ spiedContext.sendOrderedBroadcast(intent, receiverPermission, appOp, resultReceiver,
+ scheduler,
+ initialCode, initialData, initialExtras);
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+ if (binder.callingPid != SYSTEM_PID) {
+ // Unless called as the system process, can only call if the target user is the
+ // calling user.
+ // (The actual check is more complex; we may need to change it later.)
+ Assert.assertEquals(UserHandle.getUserId(binder.getCallingUid()), user.getIdentifier());
+ }
+
+ spiedContext.sendBroadcastAsUser(intent, user);
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) {
+ spiedContext.sendBroadcastAsUser(intent, user, receiverPermission);
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+ int appOp) {
+ spiedContext.sendBroadcastAsUser(intent, user, receiverPermission, appOp);
+ }
+
+ @Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
+ int initialCode, String initialData, Bundle initialExtras) {
+ spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver,
+ scheduler, initialCode, initialData, initialExtras);
+ }
+
+ @Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+ spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp,
+ resultReceiver,
+ scheduler, initialCode, initialData, initialExtras);
+ }
+
+ @Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+ spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, options,
+ resultReceiver, scheduler, initialCode, initialData, initialExtras);
+ }
+
+ @Override
+ public void sendStickyBroadcast(Intent intent) {
+ spiedContext.sendStickyBroadcast(intent);
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+ spiedContext.sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode,
+ initialData, initialExtras);
+ }
+
+ @Override
+ public void removeStickyBroadcast(Intent intent) {
+ spiedContext.removeStickyBroadcast(intent);
+ }
+
+ @Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ spiedContext.sendStickyBroadcastAsUser(intent, user);
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ spiedContext.sendStickyOrderedBroadcastAsUser(intent, user, resultReceiver, scheduler, initialCode,
+ initialData, initialExtras);
+ }
+
+ @Override
+ public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ spiedContext.removeStickyBroadcastAsUser(intent, user);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ return spiedContext.registerReceiver(receiver, filter);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ return spiedContext.registerReceiver(receiver, filter, broadcastPermission, scheduler);
+ }
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ return spiedContext.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+ scheduler);
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ spiedContext.unregisterReceiver(receiver);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 445260b..6f9f6ab 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -19,14 +19,25 @@
import android.content.Context;
import android.test.AndroidTestCase;
-public class DpmTestBase extends AndroidTestCase {
- private DpmMockContext mMockContext;
+import java.io.File;
+
+public abstract class DpmTestBase extends AndroidTestCase {
+ public static final String TAG = "DpmTest";
+
+ protected Context mRealTestContext;
+ protected DpmMockContext mMockContext;
+
+ public File dataDir;
@Override
protected void setUp() throws Exception {
super.setUp();
+ mRealTestContext = super.getContext();
mMockContext = new DpmMockContext(super.getContext());
+
+ dataDir = new File(mRealTestContext.getCacheDir(), "test-data");
+ DpmTestUtils.clearDir(dataDir);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
new file mode 100644
index 0000000..44a851a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.os.FileUtils;
+import android.util.Log;
+import android.util.Printer;
+
+import org.junit.Assert;
+
+import java.io.File;
+import java.util.List;
+
+public class DpmTestUtils {
+ private DpmTestUtils() {
+ }
+
+ public static void clearDir(File dir) {
+ if (dir.exists()) {
+ Assert.assertTrue("failed to delete dir", FileUtils.deleteContents(dir));
+ }
+ dir.mkdirs();
+ Log.i(DpmTestBase.TAG, "Created " + dir);
+ }
+
+ public static int getListSizeAllowingNull(List<?> list) {
+ return list == null ? 0 : list.size();
+ }
+
+ public static Printer LOG_PRINTER = new Printer() {
+ @Override
+ public void println(String x) {
+ Log.i(DpmTestBase.TAG, x);
+ }
+ };
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
new file mode 100644
index 0000000..08293a2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class DummyDeviceAdmins {
+ public static class Admin1 extends DeviceAdminReceiver {
+ }
+ public static class Admin2 extends DeviceAdminReceiver {
+ }
+ public static class Admin3 extends DeviceAdminReceiver {
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
new file mode 100644
index 0000000..5008fbf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import com.google.common.base.Objects;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.mockito.Mockito;
+
+public class MockUtils {
+ private MockUtils() {
+ }
+
+ public static UserHandle checkUserHandle(final int userId) {
+ final Matcher<UserHandle> m = new BaseMatcher<UserHandle>() {
+ @Override
+ public boolean matches(Object item) {
+ if (item == null) return false;
+ return Objects.equal(((UserHandle) item).getIdentifier(), userId);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("UserHandle: user-id= \"" + userId + "\"");
+ }
+ };
+ return Mockito.argThat(m);
+ }
+
+ public static Intent checkIntentComponent(final ComponentName component) {
+ final Matcher<Intent> m = new BaseMatcher<Intent>() {
+ @Override
+ public boolean matches(Object item) {
+ if (item == null) return false;
+ return Objects.equal(((Intent) item).getComponent(), component);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Intent: component=\"" + component + "\"");
+ }
+ };
+ return Mockito.argThat(m);
+ }
+
+ public static Intent checkIntentAction(final String action) {
+ final Matcher<Intent> m = new BaseMatcher<Intent>() {
+ @Override
+ public boolean matches(Object item) {
+ if (item == null) return false;
+ return Objects.equal(((Intent) item).getAction(), action);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Intent: action=\"" + action + "\"");
+ }
+ };
+ return Mockito.argThat(m);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 3b88fb1..a07d615 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -16,12 +16,11 @@
package com.android.server.devicepolicy;
+import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable;
+
import android.content.ComponentName;
-import android.content.Context;
import android.content.pm.UserInfo;
-import android.os.FileUtils;
import android.os.UserHandle;
-import android.test.AndroidTestCase;
import android.util.Log;
import java.io.BufferedReader;
@@ -31,8 +30,6 @@
import java.io.InputStreamReader;
import java.util.ArrayList;
-import junit.framework.Assert;
-
import static org.mockito.Mockito.when;
/**
@@ -47,58 +44,11 @@
(mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
*/
public class OwnersTest extends DpmTestBase {
- private static final String TAG = "DeviceOwnerTest";
-
- private static final String LEGACY_FILE = "legacy.xml";
- private static final String DEVICE_OWNER_FILE = "device_owner2.xml";
- private static final String PROFILE_OWNER_FILE_BASE = "profile_owner.xml";
-
- private File mDataDir;
-
- private class OwnersSub extends Owners {
- final File mLegacyFile;
- final File mDeviceOwnerFile;
- final File mProfileOwnerBase;
-
- public OwnersSub() {
- super(getContext());
- mLegacyFile = new File(mDataDir, LEGACY_FILE);
- mDeviceOwnerFile = new File(mDataDir, DEVICE_OWNER_FILE);
- mProfileOwnerBase = new File(mDataDir, PROFILE_OWNER_FILE_BASE);
- }
-
- @Override
- File getLegacyConfigFileWithTestOverride() {
- return mLegacyFile;
- }
-
- @Override
- File getDeviceOwnerFileWithTestOverride() {
- return mDeviceOwnerFile;
- }
-
- @Override
- File getProfileOwnerFileWithTestOverride(int userId) {
- return new File(mDeviceOwnerFile.getAbsoluteFile() + "-" + userId);
- }
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mDataDir = new File(getContext().getCacheDir(), "OwnersTest");
- if (mDataDir.exists()) {
- assertTrue("failed to delete dir", FileUtils.deleteContents(mDataDir));
- }
- mDataDir.mkdirs();
- Log.i(TAG, "Created " + mDataDir);
- }
-
private String readAsset(String assetPath) throws IOException {
final StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(
- new InputStreamReader((getContext().getResources().getAssets().open(assetPath))))) {
+ new InputStreamReader(
+ mRealTestContext.getResources().getAssets().open(assetPath)))) {
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
@@ -126,7 +76,7 @@
ui.id = userId;
userInfos.add(ui);
}
- when(getContext().getMockUserManager().getUsers()).thenReturn(userInfos);
+ when(getContext().userManager.getUsers()).thenReturn(userInfos);
}
public void testUpgrade01() throws Exception {
@@ -134,9 +84,10 @@
// First, migrate.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
- createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test01/input.xml"));
+ createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+ readAsset("OwnersTest/test01/input.xml"));
owners.load();
@@ -160,7 +111,7 @@
// Then re-read and check.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
owners.load();
assertFalse(owners.hasDeviceOwner());
@@ -176,9 +127,10 @@
// First, migrate.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
- createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test02/input.xml"));
+ createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+ readAsset("OwnersTest/test02/input.xml"));
owners.load();
@@ -204,7 +156,7 @@
// Then re-read and check.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
owners.load();
assertTrue(owners.hasDeviceOwner());
@@ -223,9 +175,10 @@
// First, migrate.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
- createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test03/input.xml"));
+ createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+ readAsset("OwnersTest/test03/input.xml"));
owners.load();
@@ -259,7 +212,7 @@
// Then re-read and check.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
owners.load();
assertFalse(owners.hasDeviceOwner());
@@ -286,9 +239,10 @@
// First, migrate.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
- createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test04/input.xml"));
+ createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+ readAsset("OwnersTest/test04/input.xml"));
owners.load();
@@ -327,7 +281,7 @@
// Then re-read and check.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
owners.load();
assertTrue(owners.hasDeviceOwner());
@@ -359,9 +313,10 @@
// First, migrate.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
- createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test05/input.xml"));
+ createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+ readAsset("OwnersTest/test05/input.xml"));
owners.load();
@@ -386,7 +341,7 @@
// Then re-read and check.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
owners.load();
assertFalse(owners.hasDeviceOwner());
@@ -405,9 +360,10 @@
// First, migrate.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
- createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test06/input.xml"));
+ createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+ readAsset("OwnersTest/test06/input.xml"));
owners.load();
@@ -431,7 +387,7 @@
// Then re-read and check.
{
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
owners.load();
assertFalse(owners.hasDeviceOwner());
@@ -447,10 +403,11 @@
public void testRemoveExistingFiles() throws Exception {
addUsersToUserManager(10, 11, 20, 21);
- final OwnersSub owners = new OwnersSub();
+ final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
// First, migrate to create new-style config files.
- createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test04/input.xml"));
+ createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+ readAsset("OwnersTest/test04/input.xml"));
owners.load();
diff --git a/services/usage/java/com/android/server/usage/UnixCalendar.java b/services/usage/java/com/android/server/usage/UnixCalendar.java
index ce06a91..db7b42d 100644
--- a/services/usage/java/com/android/server/usage/UnixCalendar.java
+++ b/services/usage/java/com/android/server/usage/UnixCalendar.java
@@ -15,40 +15,22 @@
*/
package com.android.server.usage;
-import android.app.usage.UsageStatsManager;
-
/**
* A handy calendar object that knows nothing of Locale's or TimeZones. This simplifies
* interval book-keeping. It is *NOT* meant to be used as a user-facing calendar, as it has
* no concept of Locale or TimeZone.
*/
public class UnixCalendar {
- private static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
- private static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
- private static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
- private static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
+ public static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
+ public static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
+ public static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
+ public static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
private long mTime;
public UnixCalendar(long time) {
mTime = time;
}
- public void truncateToDay() {
- mTime -= mTime % DAY_IN_MILLIS;
- }
-
- public void truncateToWeek() {
- mTime -= mTime % WEEK_IN_MILLIS;
- }
-
- public void truncateToMonth() {
- mTime -= mTime % MONTH_IN_MILLIS;
- }
-
- public void truncateToYear() {
- mTime -= mTime % YEAR_IN_MILLIS;
- }
-
public void addDays(int val) {
mTime += val * DAY_IN_MILLIS;
}
@@ -72,28 +54,4 @@
public long getTimeInMillis() {
return mTime;
}
-
- public static void truncateTo(UnixCalendar calendar, int intervalType) {
- switch (intervalType) {
- case UsageStatsManager.INTERVAL_YEARLY:
- calendar.truncateToYear();
- break;
-
- case UsageStatsManager.INTERVAL_MONTHLY:
- calendar.truncateToMonth();
- break;
-
- case UsageStatsManager.INTERVAL_WEEKLY:
- calendar.truncateToWeek();
- break;
-
- case UsageStatsManager.INTERVAL_DAILY:
- calendar.truncateToDay();
- break;
-
- default:
- throw new UnsupportedOperationException("Can't truncate date to interval " +
- intervalType);
- }
- }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index f8ae03f..0ca4bd8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -350,23 +350,6 @@
}
/**
- * Get the time at which the latest stats begin for this interval type.
- */
- public long getLatestUsageStatsBeginTime(int intervalType) {
- synchronized (mLock) {
- if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
- throw new IllegalArgumentException("Bad interval type " + intervalType);
- }
-
- final int statsFileCount = mSortedStatFiles[intervalType].size();
- if (statsFileCount > 0) {
- return mSortedStatFiles[intervalType].keyAt(statsFileCount - 1);
- }
- return -1;
- }
- }
-
- /**
* Figures out what to extract from the given IntervalStats object.
*/
interface StatCombiner<T> {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index b07b815..5188e5f 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -65,6 +65,11 @@
private final String mLogPrefix;
private final int mUserId;
+ private static final long[] INTERVAL_LENGTH = new long[] {
+ UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
+ UnixCalendar.MONTH_IN_MILLIS, UnixCalendar.YEAR_IN_MILLIS
+ };
+
interface StatsUpdatedListener {
void onStatsUpdated();
}
@@ -104,18 +109,12 @@
// By calling loadActiveStats, we will
// generate new stats for each bucket.
- loadActiveStats(currentTimeMillis,/*force=*/ false, /*resetBeginIdleTime=*/ false);
+ loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
} else {
// Set up the expiry date to be one day from the latest daily stat.
// This may actually be today and we will rollover on the first event
// that is reported.
- mDailyExpiryDate.setTimeInMillis(
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
- mDailyExpiryDate.addDays(1);
- mDailyExpiryDate.truncateToDay();
- Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
- sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) +
- "(" + mDailyExpiryDate.getTimeInMillis() + ")");
+ updateRolloverDeadline();
}
// Now close off any events that were open at the time this was saved.
@@ -170,7 +169,7 @@
void onTimeChanged(long oldTime, long newTime, boolean resetBeginIdleTime) {
persistActiveStats();
mDatabase.onTimeChanged(newTime - oldTime);
- loadActiveStats(newTime, /* force= */ true, resetBeginIdleTime);
+ loadActiveStats(newTime, resetBeginIdleTime);
}
void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -237,7 +236,7 @@
new StatCombiner<UsageStats>() {
@Override
public void combine(IntervalStats stats, boolean mutable,
- List<UsageStats> accResult) {
+ List<UsageStats> accResult) {
if (!mutable) {
accResult.addAll(stats.packageStats.values());
return;
@@ -254,7 +253,7 @@
new StatCombiner<ConfigurationStats>() {
@Override
public void combine(IntervalStats stats, boolean mutable,
- List<ConfigurationStats> accResult) {
+ List<ConfigurationStats> accResult) {
if (!mutable) {
accResult.addAll(stats.configurations.values());
return;
@@ -448,7 +447,7 @@
persistActiveStats();
mDatabase.prune(currentTimeMillis);
- loadActiveStats(currentTimeMillis, /*force=*/ false, /*resetBeginIdleTime=*/ false);
+ loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
final int continueCount = continuePreviousDay.size();
for (int i = 0; i < continueCount; i++) {
@@ -474,45 +473,28 @@
}
}
- /**
- * @param force To force all in-memory stats to be reloaded.
- */
- private void loadActiveStats(final long currentTimeMillis, boolean force,
- boolean resetBeginIdleTime) {
- final UnixCalendar tempCal = mDailyExpiryDate;
+ private void loadActiveStats(final long currentTimeMillis, boolean resetBeginIdleTime) {
for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
- tempCal.setTimeInMillis(currentTimeMillis);
- UnixCalendar.truncateTo(tempCal, intervalType);
-
- if (!force && mCurrentStats[intervalType] != null &&
- mCurrentStats[intervalType].beginTime == tempCal.getTimeInMillis()) {
- // These are the same, no need to load them (in memory stats are always newer
- // than persisted stats).
- continue;
- }
-
- final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(intervalType);
- if (lastBeginTime >= tempCal.getTimeInMillis()) {
+ final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
+ if (stats != null && currentTimeMillis - 500 >= stats.endTime &&
+ currentTimeMillis < stats.beginTime + INTERVAL_LENGTH[intervalType]) {
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
- sDateFormat.format(lastBeginTime) + "(" + lastBeginTime +
+ sDateFormat.format(stats.beginTime) + "(" + stats.beginTime +
") for interval " + intervalType);
}
- mCurrentStats[intervalType] = mDatabase.getLatestUsageStats(intervalType);
+ mCurrentStats[intervalType] = stats;
} else {
- mCurrentStats[intervalType] = null;
- }
-
- if (mCurrentStats[intervalType] == null) {
+ // No good fit remains.
if (DEBUG) {
Slog.d(TAG, "Creating new stats @ " +
- sDateFormat.format(tempCal.getTimeInMillis()) + "(" +
- tempCal.getTimeInMillis() + ") for interval " + intervalType);
-
+ sDateFormat.format(currentTimeMillis) + "(" +
+ currentTimeMillis + ") for interval " + intervalType);
}
+
mCurrentStats[intervalType] = new IntervalStats();
- mCurrentStats[intervalType].beginTime = tempCal.getTimeInMillis();
- mCurrentStats[intervalType].endTime = currentTimeMillis;
+ mCurrentStats[intervalType].beginTime = currentTimeMillis;
+ mCurrentStats[intervalType].endTime = currentTimeMillis + 1;
}
if (resetBeginIdleTime) {
@@ -522,12 +504,16 @@
}
}
mStatsChanged = false;
- mDailyExpiryDate.setTimeInMillis(currentTimeMillis);
+ updateRolloverDeadline();
+ }
+
+ private void updateRolloverDeadline() {
+ mDailyExpiryDate.setTimeInMillis(
+ mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
mDailyExpiryDate.addDays(1);
- mDailyExpiryDate.truncateToDay();
Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
- tempCal.getTimeInMillis() + ")");
+ mDailyExpiryDate.getTimeInMillis() + ")");
}
//
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index b021e80..7f813ec 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -235,7 +235,8 @@
final StorageManager storageManager = StorageManager.from(mContext);
final StorageVolume primary = storageManager.getPrimaryVolume();
massStorageSupported = primary != null && primary.allowMassStorage();
- mUseUsbNotification = !massStorageSupported;
+ mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_usbChargingMessage);
// make sure the ADB_ENABLED setting value matches the current state
try {
@@ -418,10 +419,9 @@
private boolean setUsbConfig(String config) {
if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
// set the new configuration
- String oldConfig = SystemProperties.get(USB_CONFIG_PROPERTY);
- if (!config.equals(oldConfig)) {
- SystemProperties.set(USB_CONFIG_PROPERTY, config);
- }
+ // we always set it due to b/23631400, where adbd was getting killed
+ // and not restarted due to property timeouts on some devices
+ SystemProperties.set(USB_CONFIG_PROPERTY, config);
return waitForState(config);
}
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 2cf42f0..154cbd3 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -517,7 +517,7 @@
com.android.internal.R.bool.config_disableUsbPermissionDialogs);
synchronized (mLock) {
- if (UserHandle.OWNER.equals(user)) {
+ if (UserHandle.SYSTEM.equals(user)) {
upgradeSingleUserLocked();
}
readSettingsLocked();
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 77fdb65..094b3a9 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -65,6 +65,7 @@
private final List<Connection> mUnmodifiableConferenceableConnections =
Collections.unmodifiableList(mConferenceableConnections);
+ private String mTelecomCallId;
private PhoneAccountHandle mPhoneAccount;
private CallAudioState mCallAudioState;
private int mState = Connection.STATE_NEW;
@@ -94,6 +95,26 @@
}
/**
+ * Returns the telecom internal call ID associated with this conference.
+ *
+ * @return The telecom call ID.
+ * @hide
+ */
+ public final String getTelecomCallId() {
+ return mTelecomCallId;
+ }
+
+ /**
+ * Sets the telecom internal call ID associated with this conference.
+ *
+ * @param telecomCallId The telecom call ID.
+ * @hide
+ */
+ public final void setTelecomCallId(String telecomCallId) {
+ mTelecomCallId = telecomCallId;
+ }
+
+ /**
* Returns the {@link PhoneAccountHandle} the conference call is being placed through.
*
* @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 430760a..4115756 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1074,6 +1074,8 @@
private final List<Conferenceable> mUnmodifiableConferenceables =
Collections.unmodifiableList(mConferenceables);
+ // The internal telecom call ID associated with this connection.
+ private String mTelecomCallId;
private int mState = STATE_NEW;
private CallAudioState mCallAudioState;
private Uri mAddress;
@@ -1098,6 +1100,17 @@
public Connection() {}
/**
+ * Returns the Telecom internal call ID associated with this connection. Should only be used
+ * for debugging and tracing purposes.
+ *
+ * @return The Telecom call ID.
+ * @hide
+ */
+ public final String getTelecomCallId() {
+ return mTelecomCallId;
+ }
+
+ /**
* @return The address (e.g., phone number) to which this Connection is currently communicating.
*/
public final Uri getAddress() {
@@ -1259,6 +1272,17 @@
}
/**
+ * Sets the telecom call ID associated with this Connection. The Telecom Call ID should be used
+ * ONLY for debugging purposes.
+ *
+ * @param callId The telecom call ID.
+ * @hide
+ */
+ public void setTelecomCallId(String callId) {
+ mTelecomCallId = callId;
+ }
+
+ /**
* Inform this Connection that the state of its audio output has been changed externally.
*
* @param state The new audio state.
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 6863214..aba38fe 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -32,6 +32,7 @@
private final Uri mAddress;
private final Bundle mExtras;
private final int mVideoState;
+ private final String mTelecomCallId;
/**
* @param accountHandle The accountHandle which should be used to place the call.
@@ -42,7 +43,7 @@
PhoneAccountHandle accountHandle,
Uri handle,
Bundle extras) {
- this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY);
+ this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null);
}
/**
@@ -56,10 +57,28 @@
Uri handle,
Bundle extras,
int videoState) {
+ this(accountHandle, handle, extras, videoState, null);
+ }
+
+ /**
+ * @param accountHandle The accountHandle which should be used to place the call.
+ * @param handle The handle (e.g., phone number) to which the {@link Connection} is to connect.
+ * @param extras Application-specific extra data.
+ * @param videoState Determines the video state for the connection.
+ * @param telecomCallId The telecom call ID.
+ * @hide
+ */
+ public ConnectionRequest(
+ PhoneAccountHandle accountHandle,
+ Uri handle,
+ Bundle extras,
+ int videoState,
+ String telecomCallId) {
mAccountHandle = accountHandle;
mAddress = handle;
mExtras = extras;
mVideoState = videoState;
+ mTelecomCallId = telecomCallId;
}
private ConnectionRequest(Parcel in) {
@@ -67,6 +86,7 @@
mAddress = in.readParcelable(getClass().getClassLoader());
mExtras = in.readParcelable(getClass().getClassLoader());
mVideoState = in.readInt();
+ mTelecomCallId = in.readString();
}
/**
@@ -99,6 +119,16 @@
return mVideoState;
}
+ /**
+ * Returns the internal Telecom ID associated with the connection request.
+ *
+ * @return The Telecom ID.
+ * @hide
+ */
+ public String getTelecomCallId() {
+ return mTelecomCallId;
+ }
+
@Override
public String toString() {
return String.format("ConnectionRequest %s %s",
@@ -134,5 +164,6 @@
destination.writeParcelable(mAddress, 0);
destination.writeParcelable(mExtras, 0);
destination.writeInt(mVideoState);
+ destination.writeString(mTelecomCallId);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 4e330bdb..6223495 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -116,6 +116,8 @@
private boolean mAreAccountsInitialized = false;
private Conference sNullConference;
+ private Object mIdSyncRoot = new Object();
+ private int mId = 0;
private final IBinder mBinder = new IConnectionService.Stub() {
@Override
@@ -629,7 +631,8 @@
boolean isIncoming,
boolean isUnknown) {
Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
- "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request, isIncoming,
+ "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,
+ isIncoming,
isUnknown);
Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
@@ -641,6 +644,7 @@
new DisconnectCause(DisconnectCause.ERROR));
}
+ connection.setTelecomCallId(callId);
if (connection.getState() != Connection.STATE_DISCONNECTED) {
addConnection(callId, connection);
}
@@ -953,6 +957,7 @@
connectionIds.add(mIdByConnection.get(connection));
}
}
+ conference.setTelecomCallId(id);
ParcelableConference parcelableConference = new ParcelableConference(
conference.getPhoneAccountHandle(),
conference.getState(),
@@ -989,7 +994,7 @@
public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
Connection connection) {
- String id = addExistingConnectionInternal(connection);
+ String id = addExistingConnectionInternal(phoneAccountHandle, connection);
if (id != null) {
List<String> emptyList = new ArrayList<>(0);
@@ -1151,18 +1156,29 @@
}
/**
- * Adds an existing connection to the list of connections, identified by a new UUID.
+ * Adds an existing connection to the list of connections, identified by a new call ID unique
+ * to this connection service.
*
* @param connection The connection.
- * @return The UUID of the connection (e.g. the call-id).
+ * @return The ID of the connection (e.g. the call-id).
*/
- private String addExistingConnectionInternal(Connection connection) {
- String id = UUID.randomUUID().toString();
+ private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) {
+ String id;
+ if (handle == null) {
+ // If no phone account handle was provided, we cannot be sure the call ID is unique,
+ // so just use a random UUID.
+ id = UUID.randomUUID().toString();
+ } else {
+ // Phone account handle was provided, so use the ConnectionService class name as a
+ // prefix for a unique incremental call ID.
+ id = handle.getComponentName().getClassName() + "@" + getNextCallId();
+ }
addConnection(id, connection);
return id;
}
private void addConnection(String callId, Connection connection) {
+ connection.setTelecomCallId(callId);
mConnectionById.put(callId, connection);
mIdByConnection.put(connection, callId);
connection.addConnectionListener(mConnectionListener);
@@ -1183,6 +1199,9 @@
if (mIdByConference.containsKey(conference)) {
Log.w(this, "Re-adding an existing conference: %s.", conference);
} else if (conference != null) {
+ // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we
+ // cannot determine a ConnectionService class name to associate with the ID, so use
+ // a unique UUID (for now).
String id = UUID.randomUUID().toString();
mConferenceById.put(id, conference);
mIdByConference.put(conference, id);
@@ -1284,4 +1303,15 @@
conference.onDisconnect();
}
}
+
+ /**
+ * Retrieves the next call ID as maintainted by the connection service.
+ *
+ * @return The call ID.
+ */
+ private int getNextCallId() {
+ synchronized(mIdSyncRoot) {
+ return ++mId;
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8779462..a8f2aca 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -14,6 +14,7 @@
package android.telecom;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
@@ -116,6 +117,15 @@
"android.telecom.action.PHONE_ACCOUNT_REGISTERED";
/**
+ * The {@link android.content.Intent} action used indicate that a phone account was
+ * just unregistered.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_PHONE_ACCOUNT_UNREGISTERED =
+ "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
+
+ /**
* Activity action: Shows a dialog asking the user whether or not they want to replace the
* current default Dialer with the one specified in
* {@link #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME}.
@@ -472,9 +482,12 @@
* <p>
* If no {@link PhoneAccount} fits the criteria above, this method will return {@code null}.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @param uriScheme The URI scheme.
* @return The {@link PhoneAccountHandle} corresponding to the account to be used.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
try {
if (isServiceConnected()) {
@@ -606,9 +619,12 @@
* calls. The returned list includes only those accounts which have been explicitly enabled
* by the user.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @see #EXTRA_PHONE_ACCOUNT_HANDLE
* @return A list of {@code PhoneAccountHandle} objects.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
return getCallCapablePhoneAccounts(false);
}
@@ -881,9 +897,12 @@
* Return whether a given phone number is the configured voicemail number for a
* particular phone account.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @param accountHandle The handle for the account to check the voicemail number against
* @param number The number to look up.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
try {
if (isServiceConnected()) {
@@ -899,10 +918,13 @@
/**
* Return the voicemail number for a given phone account.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @param accountHandle The handle for the phone account.
* @return The voicemail number for the phone account, and {@code null} if one has not been
* configured.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getVoiceMailNumber(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
@@ -918,9 +940,12 @@
/**
* Return the line 1 phone number for given phone account.
*
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
* @param accountHandle The handle for the account retrieve a number for.
* @return A string representation of the line 1 phone number.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getLine1Number(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
@@ -940,6 +965,7 @@
* Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
* </p>
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public boolean isInCall() {
try {
if (isServiceConnected()) {
@@ -1031,7 +1057,10 @@
/**
* Silences the ringer if a ringing call exists.
+ *
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void silenceRinger() {
try {
if (isServiceConnected()) {
@@ -1136,9 +1165,12 @@
* Requires that the method-caller be set as the system dialer app.
* </p>
*
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ *
* @param dialString The digits to dial.
* @return True if the digits were processed as an MMI code, false otherwise.
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean handleMmi(String dialString) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1159,10 +1191,13 @@
* Requires that the method-caller be set as the system dialer app.
* </p>
*
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ *
* @param accountHandle The handle for the account the MMI code should apply to.
* @param dialString The digits to dial.
* @return True if the digits were processed as an MMI code, false otherwise.
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean handleMmi(String dialString, PhoneAccountHandle accountHandle) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1177,11 +1212,14 @@
}
/**
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ *
* @param accountHandle The handle for the account to derive an adn query URI for or
* {@code null} to return a URI which will use the default account.
* @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount}
* for the the content retrieve.
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle) {
ITelecomService service = getTelecomService();
if (service != null && accountHandle != null) {
@@ -1199,7 +1237,10 @@
* <p>
* Requires that the method-caller be set as the system dialer app.
* </p>
+ *
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void cancelMissedCallsNotification() {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1221,6 +1262,7 @@
*
* @param showDialpad Brings up the in-call dialpad as part of showing the in-call screen.
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public void showInCallScreen(boolean showDialpad) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1262,6 +1304,7 @@
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
+ @RequiresPermission(android.Manifest.permission.CALL_PHONE)
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 5ac95b3..e57964a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -78,6 +78,15 @@
public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
/**
+ * Flag to require or skip entitlement checks.
+ * If true, entitlement checks will be executed if device has been configured for it,
+ * If false, entitlement checks will be skipped.
+ * @hide
+ */
+ public static final String
+ KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
+
+ /**
* If true, enable vibration (haptic feedback) for key presses in the EmergencyDialer activity.
* The pattern is set on a per-platform basis using config_virtualKeyVibePattern. To be
* consistent with the regular Dialer, this value should agree with the corresponding values
@@ -284,6 +293,17 @@
"carrier_instant_lettering_invalid_chars_string";
/**
+ * When IMS instant lettering is available for a carrier (see
+ * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines a list of characters which
+ * must be escaped with a backslash '\' character. Should be specified as a string containing
+ * the characters to be escaped. For example to escape quote and backslash the string would be
+ * a quote and a backslash.
+ * @hide
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING =
+ "carrier_instant_lettering_escaped_chars_string";
+
+ /**
* If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
* this is the value that should be used instead. A configuration value of
* RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
@@ -306,6 +326,14 @@
public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL =
"carrier_force_disable_etws_cmas_test_bool";
+ /**
+ * The default flag specifying whether "Turn on Notifications" option will be always shown in
+ * Settings->More->Emergency broadcasts menu regardless developer options is turned on or not.
+ * @hide
+ */
+ public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL =
+ "always_show_emergency_alert_onoff_bool";
+
/* The following 3 fields are related to carrier visual voicemail. */
/**
@@ -364,6 +392,14 @@
* sends out successive DTMF tones on the network.
* @hide
*/
+ public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
+
+ /**
+ * Specifies the amount of gap to be added in millis between DTMF tones. When a non-zero value
+ * is specified, the UE shall wait for the specified amount of time before it sends out
+ * successive DTMF tones on the network.
+ * @hide
+ */
public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
/**
@@ -394,6 +430,12 @@
public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
/**
+ * Determine whether preferred network type can be shown.
+ * @hide
+ */
+ public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
+
+ /**
* If this is true, the SIM card (through Customer Service Profile EF file) will be able to
* prevent manual operator selection. If false, this SIM setting will be ignored and manual
* operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -465,6 +507,7 @@
sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
+ sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false);
sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
@@ -485,6 +528,7 @@
sDefaults.putBoolean(KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL, false);
sDefaults.putBoolean(KEY_VOICE_PRIVACY_DISABLE_UI_BOOL, false);
sDefaults.putBoolean(KEY_WORLD_PHONE_BOOL, false);
+ sDefaults.putBoolean(KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
sDefaults.putInt(KEY_VOLTE_REPLACEMENT_RAT_INT, 0);
sDefaults.putString(KEY_DEFAULT_SIM_CALL_MANAGER_STRING, "");
sDefaults.putString(KEY_VVM_DESTINATION_NUMBER_STRING, "");
@@ -496,17 +540,20 @@
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false);
+ sDefaults.putInt(KEY_GSM_DTMF_TONE_DELAY_INT, 0);
sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
+ sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
// MMS defaults
sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f6e4bed..d22727d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3611,11 +3611,12 @@
*
* @hide
*/
- public boolean setNetworkSelectionModeManual(int subId, OperatorInfo operator) {
+ public boolean setNetworkSelectionModeManual(int subId, OperatorInfo operator,
+ boolean persistSelection) {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.setNetworkSelectionModeManual(subId, operator);
+ return telephony.setNetworkSelectionModeManual(subId, operator, persistSelection);
} catch (RemoteException ex) {
Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex);
} catch (NullPointerException ex) {
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index be7e702..59a8a67 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -249,9 +249,17 @@
// look for the custom ringtone, create from the string stored
// in the database.
+ // An empty string ("") in the database indicates a silent ringtone,
+ // and we set contactRingtoneUri = Uri.EMPTY, so that no ringtone will be played.
+ // {null} in the database indicates the default ringtone,
+ // and we set contactRingtoneUri = null, so that default ringtone will be played.
columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE);
if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
- info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
+ if (TextUtils.isEmpty(cursor.getString(columnIndex))) {
+ info.contactRingtoneUri = Uri.EMPTY;
+ } else {
+ info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
+ }
} else {
info.contactRingtoneUri = null;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 661f12d..dcece26 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -707,9 +707,13 @@
*
* @param subId the id of the subscription.
* @param operatorInfo the operator to attach to.
+ * @param persistSelection should the selection persist till reboot or its
+ * turned off? Will also result in notification being not shown to
+ * the user if the signal is lost.
* @return true if the request suceeded.
*/
- boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operator);
+ boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operator,
+ boolean persistSelection);
/**
* Set the preferred network type.
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 1ff621a..17c24af 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -63,8 +63,14 @@
public class MockPackageManager extends PackageManager {
@Override
- public PackageInfo getPackageInfo(String packageName, int flags)
- throws NameNotFoundException {
+ public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ @Override
+ public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+ throws NameNotFoundException {
throw new UnsupportedOperationException();
}
@@ -524,6 +530,14 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer,
+ int flags, String installerPackageName, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+
@Override
public void setInstallerPackageName(String targetPackage,
String installerPackageName) {
@@ -629,6 +643,15 @@
throw new UnsupportedOperationException();
}
+ /**
+ * @hide - to match hiding in superclass
+ */
+ @Override
+ public void deletePackageAsUser(
+ String packageName, IPackageDeleteObserver observer, int flags, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public void addPackageToPreferred(String packageName) {
throw new UnsupportedOperationException();
@@ -792,7 +815,15 @@
* @hide
*/
@Override
- public int installExistingPackage(String packageName)
+ public int installExistingPackage(String packageName) throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
throw new UnsupportedOperationException();
}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 5fbfe8a..ceb3993e 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -57,6 +57,7 @@
import android.content.res.Configuration;
import android.util.Log;
+
public class ActivityTestMain extends Activity {
static final String TAG = "ActivityTest";
@@ -315,7 +316,7 @@
Log.i(TAG, "Service disconnected " + name);
}
};
- if (bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+ if (bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
mConnections.add(conn);
} else {
Toast.makeText(ActivityTestMain.this, "Failed to bind",
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
index 0b43666..8085db7 100644
--- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
@@ -31,5 +31,7 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.camera_activity);
Log.i(TAG, "Activity created");
+ Log.i(TAG, "Source: "
+ + getIntent().getStringExtra("com.android.systemui.camera_launch_source"));
}
}
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
index 530fe00..242d3b2 100644
--- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
@@ -17,6 +17,7 @@
package com.google.android.test.cameraprewarm;
import android.app.Activity;
+import android.graphics.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
@@ -31,5 +32,7 @@
setContentView(R.layout.camera_activity);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
Log.i(CameraActivity.TAG, "Activity created");
+ Log.i(CameraActivity.TAG, "Source: "
+ + getIntent().getStringExtra("com.android.systemui.camera_launch_source"));
}
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 10cf5c1..b028ce6 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -944,5 +944,14 @@
</intent-filter>
</activity>
+ <activity
+ android:name="MultiProducerActivity"
+ android:label="Threads/Multiple Producers">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
new file mode 100644
index 0000000..b458d9b
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.DisplayListCanvas;
+import android.view.HardwareRenderer;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AbsoluteLayout;
+import android.widget.AbsoluteLayout.LayoutParams;
+
+public class MultiProducerActivity extends Activity implements OnClickListener {
+ private static final int DURATION = 800;
+ private View mBackgroundTarget = null;
+ private View mFrameTarget = null;
+ private View mContent = null;
+ // The width & height of our "output drawing".
+ private final int WIDTH = 900;
+ private final int HEIGHT = 600;
+ // A border width around the drawing.
+ private static final int BORDER_WIDTH = 20;
+ // The Gap between the content and the frame which should get filled on the right and bottom
+ // side by the backdrop.
+ final int CONTENT_GAP = 100;
+
+ // For debug purposes - disable drawing of frame / background.
+ private final boolean USE_FRAME = true;
+ private final boolean USE_BACK = true;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // To make things simple - we do a quick and dirty absolute layout.
+ final AbsoluteLayout layout = new AbsoluteLayout(this);
+
+ // Create the outer frame
+ if (USE_FRAME) {
+ mFrameTarget = new View(this);
+ LayoutParams frameLP = new LayoutParams(WIDTH, HEIGHT, 0, 0);
+ layout.addView(mFrameTarget, frameLP);
+ }
+
+ // Create the background which fills the gap between content and frame.
+ if (USE_BACK) {
+ mBackgroundTarget = new View(this);
+ LayoutParams backgroundLP = new LayoutParams(
+ WIDTH - 2 * BORDER_WIDTH, HEIGHT - 2 * BORDER_WIDTH,
+ BORDER_WIDTH, BORDER_WIDTH);
+ layout.addView(mBackgroundTarget, backgroundLP);
+ }
+
+ // Create the content
+ // Note: We reduce the size by CONTENT_GAP pixels on right and bottom, so that they get
+ // drawn by the backdrop.
+ mContent = new View(this);
+ mContent.setBackground(new ColorPulse(0xFFF44336, 0xFF9C27B0, null));
+ mContent.setOnClickListener(this);
+ LayoutParams contentLP = new LayoutParams(WIDTH - 2 * BORDER_WIDTH - CONTENT_GAP,
+ HEIGHT - 2 * BORDER_WIDTH - CONTENT_GAP, BORDER_WIDTH, BORDER_WIDTH);
+ layout.addView(mContent, contentLP);
+
+ setContentView(layout);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
+ if (view != null) {
+ view.post(mSetup);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
+ if (view != null) {
+ view.removeCallbacks(mSetup);
+ }
+ if (mBgRenderer != null) {
+ mBgRenderer.destroy();
+ mBgRenderer = null;
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ sBlockThread.run();
+ }
+
+ private Runnable mSetup = new Runnable() {
+ @Override
+ public void run() {
+ View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
+ if (view == null) {
+ view.postDelayed(mSetup, 50);
+ }
+ HardwareRenderer renderer = view.getHardwareRenderer();
+ if (renderer == null || view.getWidth() == 0) {
+ view.postDelayed(mSetup, 50);
+ }
+ ThreadedRenderer threaded = (ThreadedRenderer) renderer;
+
+ mBgRenderer = new FakeFrame(threaded,mFrameTarget, mBackgroundTarget);
+ mBgRenderer.start();
+ }
+ };
+
+ private FakeFrame mBgRenderer;
+ private class FakeFrame extends Thread {
+ ThreadedRenderer mRenderer;
+ volatile boolean mRunning = true;
+ View mTargetFrame;
+ View mTargetBack;
+ Drawable mFrameContent;
+ Drawable mBackContent;
+ // The Z value where to place this.
+ int mZFrame;
+ int mZBack;
+ String mRenderNodeName;
+
+ FakeFrame(ThreadedRenderer renderer, View targetFrame, View targetBack) {
+ mRenderer = renderer;
+ mTargetFrame = targetFrame;
+
+ mTargetBack = targetBack;
+ mFrameContent = new ColorPulse(0xFF101010, 0xFF707070, new Rect(0, 0, WIDTH, HEIGHT));
+ mBackContent = new ColorPulse(0xFF909090, 0xFFe0e0e0, null);
+ }
+
+ @Override
+ public void run() {
+ Rect currentFrameBounds = new Rect();
+ Rect currentBackBounds = new Rect();
+ Rect newBounds = new Rect();
+ int[] surfaceOrigin = new int[2];
+ RenderNode nodeFrame = null;
+ RenderNode nodeBack = null;
+
+ // Since we are overriding the window painting logic we need to at least fill the
+ // surface with some window content (otherwise the world will go black).
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ }
+
+ if (mTargetBack != null) {
+ nodeBack = RenderNode.create("FakeBackdrop", null);
+ nodeBack.setClipToBounds(true);
+ mRenderer.addRenderNode(nodeBack, true);
+ }
+
+ if (mTargetFrame != null) {
+ nodeFrame = RenderNode.create("FakeFrame", null);
+ nodeFrame.setClipToBounds(true);
+ mRenderer.addRenderNode(nodeFrame, false);
+ }
+
+ while (mRunning) {
+ // Get the surface position to draw to within our surface.
+ surfaceOrigin[0] = 0;
+ surfaceOrigin[1] = 0;
+ // This call should be done while the rendernode's displaylist is produced.
+ // For simplicity of this test we do this before we kick off the draw.
+ mContent.getLocationInSurface(surfaceOrigin);
+ mRenderer.setContentOverdrawProtectionBounds(surfaceOrigin[0], surfaceOrigin[1],
+ surfaceOrigin[0] + mContent.getWidth(),
+ surfaceOrigin[1] + mContent.getHeight());
+ // Determine new position for frame.
+ if (nodeFrame != null) {
+ surfaceOrigin[0] = 0;
+ surfaceOrigin[1] = 0;
+ mTargetFrame.getLocationInSurface(surfaceOrigin);
+ newBounds.set(surfaceOrigin[0], surfaceOrigin[1],
+ surfaceOrigin[0] + mTargetFrame.getWidth(),
+ surfaceOrigin[1] + mTargetFrame.getHeight());
+ if (!currentFrameBounds.equals(newBounds)) {
+ currentFrameBounds.set(newBounds);
+ nodeFrame.setLeftTopRightBottom(currentFrameBounds.left,
+ currentFrameBounds.top,
+ currentFrameBounds.right, currentFrameBounds.bottom);
+ }
+
+ // Draw frame
+ DisplayListCanvas canvas = nodeFrame.start(currentFrameBounds.width(),
+ currentFrameBounds.height());
+ mFrameContent.draw(canvas);
+ nodeFrame.end(canvas);
+ }
+
+ // Determine new position for backdrop
+ if (nodeBack != null) {
+ surfaceOrigin[0] = 0;
+ surfaceOrigin[1] = 0;
+ mTargetBack.getLocationInSurface(surfaceOrigin);
+ newBounds.set(surfaceOrigin[0], surfaceOrigin[1],
+ surfaceOrigin[0] + mTargetBack.getWidth(),
+ surfaceOrigin[1] + mTargetBack.getHeight());
+ if (!currentBackBounds.equals(newBounds)) {
+ currentBackBounds.set(newBounds);
+ nodeBack.setLeftTopRightBottom(currentBackBounds.left,
+ currentBackBounds.top,
+ currentBackBounds.right, currentBackBounds.bottom);
+ }
+
+ // Draw Backdrop
+ DisplayListCanvas canvas = nodeBack.start(currentBackBounds.width(),
+ currentBackBounds.height());
+ mBackContent.draw(canvas);
+ nodeBack.end(canvas);
+ }
+
+ // we need to only render one guy - the rest will happen automatically (I think).
+ if (nodeFrame != null) {
+ mRenderer.drawRenderNode(nodeFrame);
+ }
+ if (nodeBack != null) {
+ mRenderer.drawRenderNode(nodeBack);
+ }
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {}
+ }
+ if (nodeFrame != null) {
+ mRenderer.removeRenderNode(nodeFrame);
+ }
+ if (nodeBack != null) {
+ mRenderer.removeRenderNode(nodeBack);
+ }
+ }
+
+ public void destroy() {
+ mRunning = false;
+ try {
+ join();
+ } catch (InterruptedException e) {}
+ }
+ }
+
+ private final static Runnable sBlockThread = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(DURATION);
+ } catch (InterruptedException e) {
+ }
+ }
+ };
+
+ static class ColorPulse extends Drawable {
+
+ private int mColorStart;
+ private int mColorEnd;
+ private int mStep;
+ private Rect mRect;
+ private Paint mPaint = new Paint();
+
+ public ColorPulse(int color1, int color2, Rect rect) {
+ mColorStart = color1;
+ mColorEnd = color2;
+ if (rect != null) {
+ mRect = new Rect(rect.left + BORDER_WIDTH / 2, rect.top + BORDER_WIDTH / 2,
+ rect.right - BORDER_WIDTH / 2, rect.bottom - BORDER_WIDTH / 2);
+ }
+ }
+
+ static int evaluate(float fraction, int startInt, int endInt) {
+ int startA = (startInt >> 24) & 0xff;
+ int startR = (startInt >> 16) & 0xff;
+ int startG = (startInt >> 8) & 0xff;
+ int startB = startInt & 0xff;
+
+ int endA = (endInt >> 24) & 0xff;
+ int endR = (endInt >> 16) & 0xff;
+ int endG = (endInt >> 8) & 0xff;
+ int endB = endInt & 0xff;
+
+ return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+ (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+ (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+ (int)((startB + (int)(fraction * (endB - startB))));
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ float frac = mStep / 50.0f;
+ int color = evaluate(frac, mColorStart, mColorEnd);
+ if (mRect != null && !mRect.isEmpty()) {
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(BORDER_WIDTH);
+ mPaint.setColor(color);
+ canvas.drawRect(mRect, mPaint);
+ } else {
+ canvas.drawColor(color);
+ }
+
+ mStep++;
+ if (mStep >= 50) {
+ mStep = 0;
+ int tmp = mColorStart;
+ mColorStart = mColorEnd;
+ mColorEnd = tmp;
+ }
+ invalidateSelf();
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ }
+
+ @Override
+ public int getOpacity() {
+ return mRect == null || mRect.isEmpty() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
+ }
+
+ }
+}
+
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 24df85b..0e678cd 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -11,17 +11,20 @@
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/res \
frameworks/support/v7/appcompat/res \
- frameworks/support/v7/cardview/res
+ frameworks/support/v7/cardview/res \
+ frameworks/support/v7/recyclerview/res
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--extra-packages android.support.v7.appcompat \
- --extra-packages android.support.v7.cardview
+ --extra-packages android.support.v7.cardview \
+ --extra-packages android.support.v7.recyclerview
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v4 \
android-support-v7-appcompat \
- android-support-v7-cardview
+ android-support-v7-cardview \
+ android-support-v7-recyclerview
LOCAL_PACKAGE_NAME := UiBench
diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml
index 9b3d186..7b4f01c 100644
--- a/tests/UiBench/AndroidManifest.xml
+++ b/tests/UiBench/AndroidManifest.xml
@@ -83,6 +83,30 @@
<category android:name="com.android.test.uibench.TEST" />
</intent-filter>
</activity>
+ <activity
+ android:name=".TrivialRecyclerViewActivity"
+ android:label="General/Trivial Recycler ListView" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".ActivityTransition"
+ android:label="Transitions/Activity Transition" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".ActivityTransitionDetails"
+ android:label="Transitions/Activity Transition " >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <!-- Part of ActivityTransition test above, so not in TEST category -->
+ </intent-filter>
+ </activity>
<!-- Rendering -->
<activity
diff --git a/tests/UiBench/build.gradle b/tests/UiBench/build.gradle
index deecbb6..0756a8a 100644
--- a/tests/UiBench/build.gradle
+++ b/tests/UiBench/build.gradle
@@ -32,7 +32,8 @@
dependencies {
// Dependencies enumerated specifically for platform-independent / reproducible builds.
- compile 'com.android.support:support-v4:23.0.0'
- compile 'com.android.support:appcompat-v7:23.0.0'
- compile 'com.android.support:cardview-v7:23.0.0'
+ compile 'com.android.support:support-v4:23.0.1'
+ compile 'com.android.support:appcompat-v7:23.0.1'
+ compile 'com.android.support:cardview-v7:23.0.1'
+ compile 'com.android.support:recyclerview-v7:23.0.1'
}
diff --git a/tests/UiBench/res/drawable-nodpi/ball.jpg b/tests/UiBench/res/drawable-nodpi/ball.jpg
new file mode 100644
index 0000000..2960b73
--- /dev/null
+++ b/tests/UiBench/res/drawable-nodpi/ball.jpg
Binary files differ
diff --git a/tests/UiBench/res/drawable-nodpi/block.jpg b/tests/UiBench/res/drawable-nodpi/block.jpg
new file mode 100644
index 0000000..04c22a0
--- /dev/null
+++ b/tests/UiBench/res/drawable-nodpi/block.jpg
Binary files differ
diff --git a/tests/UiBench/res/drawable-nodpi/ducky.jpg b/tests/UiBench/res/drawable-nodpi/ducky.jpg
new file mode 100644
index 0000000..830bbe3
--- /dev/null
+++ b/tests/UiBench/res/drawable-nodpi/ducky.jpg
Binary files differ
diff --git a/tests/UiBench/res/drawable-nodpi/frantic.jpg b/tests/UiBench/res/drawable-nodpi/frantic.jpg
new file mode 100644
index 0000000..4c62333
--- /dev/null
+++ b/tests/UiBench/res/drawable-nodpi/frantic.jpg
Binary files differ
diff --git a/tests/UiBench/res/drawable-nodpi/jellies.jpg b/tests/UiBench/res/drawable-nodpi/jellies.jpg
new file mode 100644
index 0000000..ee2b5c6
--- /dev/null
+++ b/tests/UiBench/res/drawable-nodpi/jellies.jpg
Binary files differ
diff --git a/tests/UiBench/res/drawable-nodpi/mug.jpg b/tests/UiBench/res/drawable-nodpi/mug.jpg
new file mode 100644
index 0000000..e149e19
--- /dev/null
+++ b/tests/UiBench/res/drawable-nodpi/mug.jpg
Binary files differ
diff --git a/tests/UiBench/res/drawable-nodpi/pencil.jpg b/tests/UiBench/res/drawable-nodpi/pencil.jpg
new file mode 100644
index 0000000..e348311
--- /dev/null
+++ b/tests/UiBench/res/drawable-nodpi/pencil.jpg
Binary files differ
diff --git a/tests/UiBench/res/drawable-nodpi/scissors.jpg b/tests/UiBench/res/drawable-nodpi/scissors.jpg
new file mode 100644
index 0000000..caf0ce8
--- /dev/null
+++ b/tests/UiBench/res/drawable-nodpi/scissors.jpg
Binary files differ
diff --git a/tests/UiBench/res/drawable-nodpi/woot.jpg b/tests/UiBench/res/drawable-nodpi/woot.jpg
new file mode 100644
index 0000000..ccaef67
--- /dev/null
+++ b/tests/UiBench/res/drawable-nodpi/woot.jpg
Binary files differ
diff --git a/tests/UiBench/res/layout/activity_transition.xml b/tests/UiBench/res/layout/activity_transition.xml
new file mode 100644
index 0000000..d4c6610
--- /dev/null
+++ b/tests/UiBench/res/layout/activity_transition.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="true"
+ android:columnCount="2"
+ android:rowCount="4">
+ <ImageView
+ android:id="@+id/ducky"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerCrop"
+ android:layout_column="0"
+ android:layout_row="0"
+ android:src="@drawable/ducky"
+ android:onClick="clicked"
+ android:transitionName="ducky"/>
+ <ImageView
+ android:id="@+id/woot"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerCrop"
+ android:src="@drawable/woot"
+ android:layout_column="1"
+ android:layout_row="0"
+ android:onClick="clicked"
+ android:transitionName="woot"/>
+ <ImageView
+ android:id="@+id/ball"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerCrop"
+ android:src="@drawable/ball"
+ android:layout_column="0"
+ android:layout_row="1"
+ android:onClick="clicked"
+ android:transitionName="ball"/>
+ <ImageView
+ android:id="@+id/block"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerCrop"
+ android:src="@drawable/block"
+ android:layout_column="1"
+ android:layout_row="1"
+ android:onClick="clicked"
+ android:transitionName="block"/>
+ <ImageView
+ android:id="@+id/jellies"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerCrop"
+ android:src="@drawable/jellies"
+ android:layout_column="0"
+ android:layout_row="2"
+ android:onClick="clicked"
+ android:transitionName="jellies"/>
+ <ImageView
+ android:id="@+id/mug"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerCrop"
+ android:src="@drawable/mug"
+ android:layout_column="1"
+ android:layout_row="2"
+ android:onClick="clicked"
+ android:transitionName="mug"/>
+ <ImageView
+ android:id="@+id/pencil"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerCrop"
+ android:src="@drawable/pencil"
+ android:layout_column="0"
+ android:layout_row="3"
+ android:onClick="clicked"
+ android:transitionName="pencil"/>
+ <ImageView
+ android:id="@+id/scissors"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerCrop"
+ android:src="@drawable/scissors"
+ android:layout_column="1"
+ android:layout_row="3"
+ android:onClick="clicked"
+ android:transitionName="scissors"/>
+</GridLayout>
\ No newline at end of file
diff --git a/tests/UiBench/res/layout/activity_transition_details.xml b/tests/UiBench/res/layout/activity_transition_details.xml
new file mode 100644
index 0000000..1022d2f
--- /dev/null
+++ b/tests/UiBench/res/layout/activity_transition_details.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@+id/titleImage"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:scaleType="centerCrop"
+ android:transitionName="hero"
+ android:onClick="clicked"/>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="2dp"
+ android:background="#808080"/>
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="Sample Picture"
+ android:textSize="30sp"
+ android:textColor="#FFF"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/UiBench/res/layout/recycler_view.xml b/tests/UiBench/res/layout/recycler_view.xml
new file mode 100644
index 0000000..54c5b58
--- /dev/null
+++ b/tests/UiBench/res/layout/recycler_view.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<android.support.v7.widget.RecyclerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/recyclerView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java b/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java
new file mode 100644
index 0000000..1106a13
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.uibench;
+
+import android.app.ActivityOptions;
+import android.app.SharedElementCallback;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.ImageView;
+
+import java.util.List;
+import java.util.Map;
+
+public class ActivityTransition extends AppCompatActivity {
+ private static final String KEY_ID = "ViewTransitionValues:id";
+
+ private ImageView mHero;
+
+ public static final int[] DRAWABLES = {
+ R.drawable.ball,
+ R.drawable.block,
+ R.drawable.ducky,
+ R.drawable.jellies,
+ R.drawable.mug,
+ R.drawable.pencil,
+ R.drawable.scissors,
+ R.drawable.woot,
+ };
+
+ public static final int[] IDS = {
+ R.id.ball,
+ R.id.block,
+ R.id.ducky,
+ R.id.jellies,
+ R.id.mug,
+ R.id.pencil,
+ R.id.scissors,
+ R.id.woot,
+ };
+
+ public static final String[] NAMES = {
+ "ball",
+ "block",
+ "ducky",
+ "jellies",
+ "mug",
+ "pencil",
+ "scissors",
+ "woot",
+ };
+
+ public static int getIdForKey(String id) {
+ return IDS[getIndexForKey(id)];
+ }
+
+ public static int getDrawableIdForKey(String id) {
+ return DRAWABLES[getIndexForKey(id)];
+ }
+
+ public static int getIndexForKey(String id) {
+ for (int i = 0; i < NAMES.length; i++) {
+ String name = NAMES[i];
+ if (name.equals(id)) {
+ return i;
+ }
+ }
+ return 2;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
+ setContentView(R.layout.activity_transition);
+ setupHero();
+ }
+
+ private void setupHero() {
+ String name = getIntent().getStringExtra(KEY_ID);
+ mHero = null;
+ if (name != null) {
+ mHero = (ImageView) findViewById(getIdForKey(name));
+ setEnterSharedElementCallback(new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List<String> names,
+ Map<String, View> sharedElements) {
+ sharedElements.put("hero", mHero);
+ }
+ });
+ }
+ }
+
+ public void clicked(View v) {
+ mHero = (ImageView) v;
+ Intent intent = new Intent(this, ActivityTransitionDetails.class);
+ intent.putExtra(KEY_ID, v.getTransitionName());
+ ActivityOptions activityOptions
+ = ActivityOptions.makeSceneTransitionAnimation(this, mHero, "hero");
+ startActivity(intent, activityOptions.toBundle());
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/ActivityTransitionDetails.java b/tests/UiBench/src/com/android/test/uibench/ActivityTransitionDetails.java
new file mode 100644
index 0000000..a654c61
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/ActivityTransitionDetails.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.uibench;
+
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.test.uibench.ActivityTransition;
+import com.android.test.uibench.R;
+
+
+public class ActivityTransitionDetails extends AppCompatActivity {
+ private static final String KEY_ID = "ViewTransitionValues:id";
+ private int mImageResourceId = R.drawable.ducky;
+ private String mName = "ducky";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().setBackgroundDrawable(new ColorDrawable(Color.DKGRAY));
+ setContentView(R.layout.activity_transition_details);
+ ImageView titleImage = (ImageView) findViewById(R.id.titleImage);
+ titleImage.setImageDrawable(getHeroDrawable());
+ }
+
+ private Drawable getHeroDrawable() {
+ String name = getIntent().getStringExtra(KEY_ID);
+ if (name != null) {
+ mName = name;
+ mImageResourceId = ActivityTransition.getDrawableIdForKey(name);
+ }
+
+ return getResources().getDrawable(mImageResourceId);
+ }
+
+ public void clicked(View v) {
+ Intent intent = new Intent(this, ActivityTransition.class);
+ intent.putExtra(KEY_ID, mName);
+ ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
+ this, v, "hero");
+ startActivity(intent, activityOptions.toBundle());
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java b/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java
index 84383ec..603244e 100644
--- a/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java
@@ -20,6 +20,8 @@
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
+import com.android.test.uibench.listview.CompatListActivity;
+
public class InflatingListActivity extends CompatListActivity {
@Override
protected ListAdapter createListAdapter() {
diff --git a/tests/UiBench/src/com/android/test/uibench/TextCacheHighHitrateActivity.java b/tests/UiBench/src/com/android/test/uibench/TextCacheHighHitrateActivity.java
index 91d74ac..fc5be35 100644
--- a/tests/UiBench/src/com/android/test/uibench/TextCacheHighHitrateActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/TextCacheHighHitrateActivity.java
@@ -18,6 +18,8 @@
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
+import com.android.test.uibench.listview.CompatListActivity;
+
public class TextCacheHighHitrateActivity extends CompatListActivity {
@Override
protected ListAdapter createListAdapter() {
diff --git a/tests/UiBench/src/com/android/test/uibench/TextCacheLowHitrateActivity.java b/tests/UiBench/src/com/android/test/uibench/TextCacheLowHitrateActivity.java
index b526cde..14dc437 100644
--- a/tests/UiBench/src/com/android/test/uibench/TextCacheLowHitrateActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/TextCacheLowHitrateActivity.java
@@ -18,6 +18,8 @@
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
+import com.android.test.uibench.listview.CompatListActivity;
+
public class TextCacheLowHitrateActivity extends CompatListActivity {
@Override
protected ListAdapter createListAdapter() {
diff --git a/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java b/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java
index 339ac80..2625a99 100644
--- a/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java
@@ -18,6 +18,8 @@
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
+import com.android.test.uibench.listview.CompatListActivity;
+
public class TrivialListActivity extends CompatListActivity {
@Override
protected ListAdapter createListAdapter() {
diff --git a/tests/UiBench/src/com/android/test/uibench/TrivialRecyclerViewActivity.java b/tests/UiBench/src/com/android/test/uibench/TrivialRecyclerViewActivity.java
new file mode 100644
index 0000000..4647ba7
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/TrivialRecyclerViewActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.uibench;
+
+import android.support.v7.widget.RecyclerView;
+
+import com.android.test.uibench.recyclerview.RvArrayAdapter;
+import com.android.test.uibench.recyclerview.RvCompatListActivity;
+
+public class TrivialRecyclerViewActivity extends RvCompatListActivity {
+ @Override
+ protected RecyclerView.Adapter createAdapter() {
+ return new RvArrayAdapter(TextUtils.buildSimpleStringList());
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/CompatListActivity.java b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
similarity index 96%
rename from tests/UiBench/src/com/android/test/uibench/CompatListActivity.java
rename to tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
index 40ec453..214c074 100644
--- a/tests/UiBench/src/com/android/test/uibench/CompatListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.test.uibench;
+package com.android.test.uibench.listview;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
diff --git a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvArrayAdapter.java b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvArrayAdapter.java
new file mode 100644
index 0000000..e5a3a49
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvArrayAdapter.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.uibench.recyclerview;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class RvArrayAdapter extends RecyclerView.Adapter<RvArrayAdapter.ViewHolder> {
+ private String[] mDataSet;
+ private LayoutInflater mLayoutInflater;
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView mTextView;
+
+ public ViewHolder(View v) {
+ super(v);
+ mTextView = (TextView) v.findViewById(android.R.id.text1);
+ }
+
+ public TextView getTextView() {
+ return mTextView;
+ }
+ }
+
+ public RvArrayAdapter(String[] dataSet) {
+ mDataSet = dataSet;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ if (mLayoutInflater == null) {
+ mLayoutInflater = LayoutInflater.from(viewGroup.getContext());
+ }
+ View v = mLayoutInflater.inflate(android.R.layout.simple_list_item_1, viewGroup, false);
+
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, final int position) {
+ viewHolder.getTextView().setText(mDataSet[position]);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mDataSet.length;
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java
new file mode 100644
index 0000000..e08dbc6
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.uibench.recyclerview;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+import com.android.test.uibench.R;
+
+public abstract class RvCompatListActivity extends AppCompatActivity {
+ public static class RecyclerViewFragment extends Fragment {
+ RecyclerView.LayoutManager layoutManager;
+ RecyclerView.Adapter adapter;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ RecyclerView recyclerView = (RecyclerView) inflater.inflate(
+ R.layout.recycler_view, container, false);
+ recyclerView.setLayoutManager(layoutManager);
+ recyclerView.setAdapter(adapter);
+ return recyclerView;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FragmentManager fm = getSupportFragmentManager();
+ if (fm.findFragmentById(android.R.id.content) == null) {
+ RecyclerViewFragment fragment = new RecyclerViewFragment();
+ fragment.layoutManager = createLayoutManager(this);
+ fragment.adapter = createAdapter();
+ fm.beginTransaction().add(android.R.id.content, fragment).commit();
+ }
+ }
+
+ protected RecyclerView.LayoutManager createLayoutManager(Context context) {
+ return new LinearLayoutManager(context);
+ }
+
+ protected abstract RecyclerView.Adapter createAdapter();
+}
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
index 705cc34..2be99be 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
@@ -21,10 +21,14 @@
<group>
<path
+ android:name="box0"
+ android:pathData="m0,0l480,0l0,480l-480,0l0-480z"
+ android:fillColor="@android:color/white" />
+ <path
android:name="box1"
android:pathData="m20,200l100,90l180-180l-35-35l-145,145l-60-60l-40,40z"
- android:fillColor="?android:attr/colorControlActivated"
- android:strokeColor="?android:attr/colorControlActivated"
+ android:fillColor="?android:attr/colorControlNormal"
+ android:strokeColor="?android:attr/colorControlNormal"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</group>
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
index a23d819..85fc452 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
@@ -18,7 +18,12 @@
import android.os.Bundle;
import android.util.Log;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.GridLayout;
@SuppressWarnings({"UnusedDeclaration"})
@@ -55,6 +60,23 @@
container.setBackgroundColor(0xFF888888);
final Button []bArray = new Button[icon.length];
+ CheckBox toggle = new CheckBox(this);
+ toggle.setText("Toggle");
+ toggle.setChecked(true);
+ toggle.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ ViewGroup vg = (ViewGroup) buttonView.getParent();
+ for (int i = 0, count = vg.getChildCount(); i < count; i++) {
+ View child = vg.getChildAt(i);
+ if (child != buttonView) {
+ child.setEnabled(isChecked);
+ }
+ }
+ }
+ });
+ container.addView(toggle);
+
for (int i = 0; i < icon.length; i++) {
Button button = new Button(this);
bArray[i] = button;
diff --git a/tests/WindowAnimationJank/Android.mk b/tests/WindowAnimationJank/Android.mk
new file mode 100644
index 0000000..888ae64
--- /dev/null
+++ b/tests/WindowAnimationJank/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WindowAnimationJank
+
+LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator ub-janktesthelper
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tests/WindowAnimationJank/AndroidManifest.xml b/tests/WindowAnimationJank/AndroidManifest.xml
new file mode 100644
index 0000000..d7aef33
--- /dev/null
+++ b/tests/WindowAnimationJank/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.windowanimationjank">
+
+ <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="ElementLayoutActivity"
+ android:label="ElementLayoutActivity"
+ android:taskAffinity="android.windowanimationjank.ElementLayoutActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="android.windowanimationjank">
+ </instrumentation>
+
+</manifest>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/tests/WindowAnimationJank/res/layout/flowlayout.xml
similarity index 60%
rename from packages/SystemUI/res/values-sw600dp-port/dimens.xml
rename to tests/WindowAnimationJank/res/layout/flowlayout.xml
index 7dc91d1..f2b559b 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/tests/WindowAnimationJank/res/layout/flowlayout.xml
@@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- * Copyright (c) 2012, The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-*/
--->
-<resources>
- <!-- Recent Applications parameters -->
- <dimen name="status_bar_recents_app_label_width">140dip</dimen>
-</resources>
+ -->
+<android.windowanimationjank.FlowLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/root_flow_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" />
\ No newline at end of file
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java b/tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java
new file mode 100644
index 0000000..3b1fabc
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.windowanimationjank;
+
+import java.util.Random;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+import android.widget.Chronometer;
+import android.widget.RadioButton;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+/*
+ * Activity with arbitrary number of random UI elements, refresh itself constantly.
+ */
+public class ElementLayoutActivity extends Activity implements OnPreDrawListener {
+ public final static String NUM_ELEMENTS_KEY = "num_elements";
+
+ private final static int DEFAULT_NUM_ELEMENTS = 100;
+ private final static int BACKGROUND_COLOR = 0xfffff000;
+ private final static int INDICATOR_COLOR = 0xffff0000;
+
+ private FlowLayout mLayout;
+ // Use the constant seed in order to get predefined order of elements.
+ private Random mRandom = new Random(0);
+ // Blinker indicator for visual feedback that Activity is currently updating.
+ private TextView mIndicator;
+ private static float mIndicatorState;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.flowlayout);
+
+ mLayout = (FlowLayout)findViewById(R.id.root_flow_layout);
+ mLayout.setBackgroundColor(BACKGROUND_COLOR);
+
+ mIndicator = new TextView(this);
+ mLayout.addView(mIndicator);
+ mIndicator.setText("***\n***");
+ mIndicator.setBackgroundColor(BACKGROUND_COLOR);
+ mIndicatorState = 0.0f;
+
+ // Need constantly invalidate view in order to get max redraw rate.
+ mLayout.getViewTreeObserver().addOnPreDrawListener(this);
+
+ // Read requested number of elements in layout.
+ int numElements = getIntent().getIntExtra(NUM_ELEMENTS_KEY, DEFAULT_NUM_ELEMENTS);
+
+ for (int i = 0; i < numElements; ++i) {
+ switch (mRandom.nextInt(5)) {
+ case 0:
+ createRadioButton();
+ break;
+ case 1:
+ createToggleButton();
+ break;
+ case 2:
+ createSwitch();
+ break;
+ case 3:
+ createTextView();
+ break;
+ case 4:
+ createChronometer();
+ break;
+ }
+ }
+
+ setContentView(mLayout);
+ }
+
+ private void createTextView() {
+ TextView textView = new TextView(this);
+ int lineCnt = mRandom.nextInt(4);
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < lineCnt; ++i) {
+ if (i != 0) {
+ buffer.append("\n");
+ }
+ buffer.append("Line:" + mRandom.nextInt());
+ }
+ textView.setText(buffer);
+ mLayout.addView(textView);
+ }
+
+ private void createRadioButton() {
+ RadioButton button = new RadioButton(this);
+ button.setText("RadioButton:" + mRandom.nextInt());
+ mLayout.addView(button);
+ }
+
+ private void createToggleButton() {
+ ToggleButton button = new ToggleButton(this);
+ button.setChecked(mRandom.nextBoolean());
+ mLayout.addView(button);
+ }
+
+ private void createSwitch() {
+ Switch button = new Switch(this);
+ button.setChecked(mRandom.nextBoolean());
+ mLayout.addView(button);
+ }
+
+ private void createChronometer() {
+ Chronometer chronometer = new Chronometer(this);
+ chronometer.setBase(mRandom.nextLong());
+ mLayout.addView(chronometer);
+ chronometer.start();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ // Interpolate indicator color
+ int background = 0xff000000;
+ for (int i = 0; i < 3; ++i) {
+ int shift = 8 * i;
+ int colorB = (BACKGROUND_COLOR >> shift) & 0xff;
+ int colorI = (INDICATOR_COLOR >> shift) & 0xff;
+ int color = (int)((float)colorB * (1.0f - mIndicatorState) +
+ (float)colorI * mIndicatorState);
+ if (color > 255) {
+ color = 255;
+ }
+ background |= (color << shift);
+ }
+
+ mIndicator.setBackgroundColor(background);
+ mIndicatorState += (3 / 60.0f); // around 3 times per second
+ mIndicatorState = mIndicatorState - (int)mIndicatorState;
+
+ mLayout.postInvalidate();
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java b/tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java
new file mode 100644
index 0000000..9a2b9cc
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.windowanimationjank;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Custom layout that place all elements in flows with and automatically wraps them.
+ */
+public class FlowLayout extends ViewGroup {
+ private int mLineHeight;
+
+ public FlowLayout(Context context) {
+ super(context);
+ }
+
+ public FlowLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int width =
+ MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() -getPaddingRight();
+ int height =
+ MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
+ final int count = getChildCount();
+
+ int x = getPaddingLeft();
+ int y = getPaddingTop();
+ int lineHeight = 0;
+
+ int childHeightMeasureSpec;
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+ } else {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ }
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+ childHeightMeasureSpec);
+ final int childWidth = child.getMeasuredWidth();
+ lineHeight = Math.max(lineHeight, child.getMeasuredHeight());
+
+ if (x + childWidth > width) {
+ x = getPaddingLeft();
+ y += lineHeight;
+ }
+
+ x += childWidth;
+ }
+ }
+ mLineHeight = lineHeight;
+
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
+ height = y + lineHeight;
+ } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+ if (y + lineHeight < height) {
+ height = y + lineHeight;
+ }
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ if (p instanceof LayoutParams) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int count = getChildCount();
+ final int width = r - l;
+ int x = getPaddingLeft();
+ int y = getPaddingTop();
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+ if (x + childWidth > width) {
+ x = getPaddingLeft();
+ y += mLineHeight;
+ }
+ child.layout(x, y, x + childWidth, y + childHeight);
+ x += childWidth;
+ }
+ }
+ }
+}
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java b/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java
new file mode 100644
index 0000000..1fb502a
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.windowanimationjank;
+
+import android.os.Bundle;
+import android.support.test.jank.JankTest;
+import android.support.test.jank.GfxMonitor;
+
+/**
+ * Detect janks during screen rotation for full-screen activity. Periodically change
+ * orientation from left to right and track ElementLayoutActivity rendering performance
+ * via GfxMonitor.
+ */
+public class FullscreenRotationTest extends WindowAnimationJankTestBase {
+ private final static int STEP_CNT = 3;
+
+ @Override
+ public void beforeTest() throws Exception {
+ getUiDevice().setOrientationLeft();
+ Utils.startElementLayout(getInstrumentation(), 100);
+ super.beforeTest();
+ }
+
+ @Override
+ public void afterTest(Bundle metrics) {
+ Utils.rotateDevice(getInstrumentation(), Utils.ROTATION_MODE_NATURAL);
+ super.afterTest(metrics);
+ }
+
+ @JankTest(expectedFrames=100, defaultIterationCount=2)
+ @GfxMonitor(processName=Utils.PACKAGE)
+ public void testRotation() throws Exception {
+ for (int i = 0; i < STEP_CNT; ++i) {
+ Utils.rotateDevice(getInstrumentation(),
+ Utils.getDeviceRotation(getInstrumentation()) == Utils.ROTATION_MODE_LEFT ?
+ Utils.ROTATION_MODE_RIGHT : Utils.ROTATION_MODE_LEFT);
+ }
+ }
+}
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
new file mode 100644
index 0000000..2531464
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.windowanimationjank;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+/**
+ * Set of helpers to manipulate test activities.
+ */
+public class Utils {
+ protected final static String PACKAGE = "android.windowanimationjank";
+ protected final static String ELEMENT_LAYOUT_ACTIVITY = "ElementLayoutActivity";
+ protected final static String ELEMENT_LAYOUT_CLASS = PACKAGE + "." + ELEMENT_LAYOUT_ACTIVITY;
+ protected final static long WAIT_FOR_ACTIVITY_TIMEOUT = 10000;
+ private static final BySelector ROOT_ELEMENT_LAYOUT = By.res(PACKAGE, "root_flow_layout");
+
+ private final static long ROTATION_ANIMATION_TIME_FULL_SCREEN_MS = 1000;
+
+ protected final static int ROTATION_MODE_NATURAL = 0;
+ protected final static int ROTATION_MODE_LEFT = 1;
+ protected final static int ROTATION_MODE_RIGHT = 2;
+
+ private static UiObject2 waitForActivity(Instrumentation instrumentation, BySelector selector) {
+ UiDevice device = UiDevice.getInstance(instrumentation);
+ UiObject2 window = device.wait(Until.findObject(selector), WAIT_FOR_ACTIVITY_TIMEOUT);
+ if (window == null) {
+ throw new RuntimeException(selector.toString() + " has not been started.");
+ }
+
+ // Get root object.
+ while (window.getParent() != null) {
+ window = window.getParent();
+ }
+ return window;
+ }
+
+ public static UiObject2 waitForElementLayout(Instrumentation instrumentation) {
+ return waitForActivity(instrumentation, ROOT_ELEMENT_LAYOUT);
+ }
+
+ /**
+ * Start and return activity with requested number of random elements.
+ */
+ public static UiObject2 startElementLayout(Instrumentation instrumentation, int numElements) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName(PACKAGE, ELEMENT_LAYOUT_CLASS));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(ElementLayoutActivity.NUM_ELEMENTS_KEY, numElements);
+ instrumentation.getTargetContext().startActivity(intent);
+ return waitForElementLayout(instrumentation);
+ }
+
+ public static int getDeviceRotation(Instrumentation instrumentation) {
+ try {
+ UiDevice device = UiDevice.getInstance(instrumentation);
+ switch (device.getDisplayRotation()) {
+ case UiAutomation.ROTATION_FREEZE_90:
+ return ROTATION_MODE_LEFT;
+ case UiAutomation.ROTATION_FREEZE_270:
+ return ROTATION_MODE_RIGHT;
+ case UiAutomation.ROTATION_FREEZE_0:
+ case UiAutomation.ROTATION_FREEZE_180:
+ return ROTATION_MODE_NATURAL;
+ }
+ } catch(Exception e) {
+ throw new RuntimeException();
+ }
+ throw new RuntimeException("Unsupported device rotation.");
+ }
+
+ public static void rotateDevice(Instrumentation instrumentation, int rotationMode) {
+ try {
+ UiDevice device = UiDevice.getInstance(instrumentation);
+ long startTime = System.currentTimeMillis();
+ switch (rotationMode) {
+ case ROTATION_MODE_NATURAL:
+ device.setOrientationNatural();
+ break;
+ case ROTATION_MODE_LEFT:
+ device.setOrientationLeft();
+ break;
+ case ROTATION_MODE_RIGHT:
+ device.setOrientationRight();
+ break;
+ default:
+ throw new RuntimeException("Unsupported rotation mode: " + rotationMode);
+ }
+
+ long toSleep = ROTATION_ANIMATION_TIME_FULL_SCREEN_MS -
+ (System.currentTimeMillis() - startTime);
+ if (toSleep > 0) {
+ SystemClock.sleep(toSleep);
+ }
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
new file mode 100644
index 0000000..bf739fa
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.windowanimationjank;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+import android.support.test.jank.JankTestBase;
+import android.support.test.uiautomator.UiDevice;
+
+/**
+ * This adds additional system level jank monitor and its result is merged with primary monitor
+ * used in test.
+ */
+public abstract class WindowAnimationJankTestBase extends JankTestBase {
+ private static final String TAG = "WindowAnimationJankTestBase";
+
+ protected WindowAnimationJankTestBase() {
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // fix device orientation
+ getUiDevice().setOrientationNatural();
+
+ // Start from the home screen
+ getUiDevice().pressHome();
+ getUiDevice().waitForIdle();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ getUiDevice().unfreezeRotation();
+ super.tearDown();
+ }
+
+ protected UiDevice getUiDevice() {
+ return UiDevice.getInstance(getInstrumentation());
+ }
+}
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 95f676e..a390b0c 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -94,7 +94,7 @@
try {
mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null,
- Configuration.EMPTY);
+ Configuration.EMPTY, false);
fail("IWindowManager.addAppToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -175,16 +175,6 @@
}
try {
- mWm.setAppWillBeHidden(null);
- fail("IWindowManager.setAppWillBeHidden did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
mWm.setAppVisibility(null, false);
fail("IWindowManager.setAppVisibility did not throw SecurityException as"
+ " expected");
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index a4e5d3d..e22e76d 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2120,7 +2120,7 @@
size_t N = symbols->getSymbols().size();
for (i=0; i<N; i++) {
const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
- if (sym.typeCode == AaptSymbolEntry::TYPE_UNKNOWN) {
+ if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
continue;
}
if (!assets->isJavaSymbol(sym, includePrivate)) {
diff --git a/tools/aapt2/Logger.h b/tools/aapt2/Logger.h
index 1d437eb..eed58b8 100644
--- a/tools/aapt2/Logger.h
+++ b/tools/aapt2/Logger.h
@@ -18,11 +18,11 @@
#define AAPT_LOGGER_H
#include "Source.h"
+#include "StringPiece.h"
#include <memory>
#include <ostream>
#include <string>
-#include <utils/String8.h>
namespace aapt {
@@ -71,11 +71,6 @@
Source mSource;
};
-inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
- android::String8 utf8(str.data(), str.size());
- return out.write(utf8.string(), utf8.size());
-}
-
} // namespace aapt
#endif // AAPT_LOGGER_H
diff --git a/tools/aapt2/StringPiece.h b/tools/aapt2/StringPiece.h
index e2a1597..8cbdeae 100644
--- a/tools/aapt2/StringPiece.h
+++ b/tools/aapt2/StringPiece.h
@@ -229,4 +229,9 @@
} // namespace aapt
+inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
+ android::String8 utf8(str.data(), str.size());
+ return out.write(utf8.string(), utf8.size());
+}
+
#endif // AAPT_STRING_PIECE_H
diff --git a/tools/aapt2/Util.cpp b/tools/aapt2/Util.cpp
index 03ecd1a..ca352e0 100644
--- a/tools/aapt2/Util.cpp
+++ b/tools/aapt2/Util.cpp
@@ -175,7 +175,51 @@
const char16_t* start = str.begin();
const char16_t* current = start;
while (current != end) {
- if (*current == u'"') {
+ if (mLastCharWasEscape) {
+ switch (*current) {
+ case u't':
+ mStr += u'\t';
+ break;
+ case u'n':
+ mStr += u'\n';
+ break;
+ case u'#':
+ mStr += u'#';
+ break;
+ case u'@':
+ mStr += u'@';
+ break;
+ case u'?':
+ mStr += u'?';
+ break;
+ case u'"':
+ mStr += u'"';
+ break;
+ case u'\'':
+ mStr += u'\'';
+ break;
+ case u'\\':
+ mStr += u'\\';
+ break;
+ case u'u': {
+ current++;
+ Maybe<char16_t> c = parseUnicodeCodepoint(¤t, end);
+ if (!c) {
+ mError = "invalid unicode escape sequence";
+ return *this;
+ }
+ mStr += c.value();
+ current -= 1;
+ break;
+ }
+
+ default:
+ // Ignore.
+ break;
+ }
+ mLastCharWasEscape = false;
+ start = current + 1;
+ } else if (*current == u'"') {
if (!mQuote && mTrailingSpace) {
// We found an opening quote, and we have
// trailing space, so we should append that
@@ -208,52 +252,7 @@
}
mStr.append(start, current - start);
start = current + 1;
-
- current++;
- if (current != end) {
- switch (*current) {
- case u't':
- mStr += u'\t';
- break;
- case u'n':
- mStr += u'\n';
- break;
- case u'#':
- mStr += u'#';
- break;
- case u'@':
- mStr += u'@';
- break;
- case u'?':
- mStr += u'?';
- break;
- case u'"':
- mStr += u'"';
- break;
- case u'\'':
- mStr += u'\'';
- break;
- case u'\\':
- mStr += u'\\';
- break;
- case u'u': {
- current++;
- Maybe<char16_t> c = parseUnicodeCodepoint(¤t, end);
- if (!c) {
- mError = "invalid unicode escape sequence";
- return *this;
- }
- mStr += c.value();
- current -= 1;
- break;
- }
-
- default:
- // Ignore.
- break;
- }
- start = current + 1;
- }
+ mLastCharWasEscape = true;
} else if (!mQuote) {
// This is not quoted text, so look for whitespace.
if (isspace16(*current)) {
diff --git a/tools/aapt2/Util.h b/tools/aapt2/Util.h
index 9cdb152..7ec6b03 100644
--- a/tools/aapt2/Util.h
+++ b/tools/aapt2/Util.h
@@ -162,6 +162,7 @@
std::u16string mStr;
bool mQuote = false;
bool mTrailingSpace = false;
+ bool mLastCharWasEscape = false;
std::string mError;
};
diff --git a/tools/aapt2/Util_test.cpp b/tools/aapt2/Util_test.cpp
index 0b08d24..92f2a1c 100644
--- a/tools/aapt2/Util_test.cpp
+++ b/tools/aapt2/Util_test.cpp
@@ -38,6 +38,13 @@
EXPECT_TRUE(util::stringStartsWith<char>("hello.xml", "he"));
}
+TEST(UtilTest, StringBuilderSplitEscapeSequence) {
+ EXPECT_EQ(StringPiece16(u"this is a new\nline."),
+ util::StringBuilder().append(u"this is a new\\")
+ .append(u"nline.")
+ .str());
+}
+
TEST(UtilTest, StringBuilderWhitespaceRemoval) {
EXPECT_EQ(StringPiece16(u"hey guys this is so cool"),
util::StringBuilder().append(u" hey guys ")
diff --git a/tools/aidl b/tools/aidl
new file mode 100644
index 0000000..8a42fa0
--- /dev/null
+++ b/tools/aidl
@@ -0,0 +1,4 @@
+Where has aidl gone?
+
+ aidl has moved to //system/tools/aidl as part of adding support for
+ generating bindings in C++.
diff --git a/tools/aidl/AST.cpp b/tools/aidl/AST.cpp
deleted file mode 100644
index bfa6765..0000000
--- a/tools/aidl/AST.cpp
+++ /dev/null
@@ -1,912 +0,0 @@
-#include "AST.h"
-#include "Type.h"
-
-void
-WriteModifiers(FILE* to, int mod, int mask)
-{
- int m = mod & mask;
-
- if (m & OVERRIDE) {
- fprintf(to, "@Override ");
- }
-
- if ((m & SCOPE_MASK) == PUBLIC) {
- fprintf(to, "public ");
- }
- else if ((m & SCOPE_MASK) == PRIVATE) {
- fprintf(to, "private ");
- }
- else if ((m & SCOPE_MASK) == PROTECTED) {
- fprintf(to, "protected ");
- }
-
- if (m & STATIC) {
- fprintf(to, "static ");
- }
-
- if (m & FINAL) {
- fprintf(to, "final ");
- }
-
- if (m & ABSTRACT) {
- fprintf(to, "abstract ");
- }
-}
-
-void
-WriteArgumentList(FILE* to, const vector<Expression*>& arguments)
-{
- size_t N = arguments.size();
- for (size_t i=0; i<N; i++) {
- arguments[i]->Write(to);
- if (i != N-1) {
- fprintf(to, ", ");
- }
- }
-}
-
-ClassElement::ClassElement()
-{
-}
-
-ClassElement::~ClassElement()
-{
-}
-
-Field::Field()
- :ClassElement(),
- modifiers(0),
- variable(NULL)
-{
-}
-
-Field::Field(int m, Variable* v)
- :ClassElement(),
- modifiers(m),
- variable(v)
-{
-}
-
-Field::~Field()
-{
-}
-
-void
-Field::GatherTypes(set<Type*>* types) const
-{
- types->insert(this->variable->type);
-}
-
-void
-Field::Write(FILE* to)
-{
- if (this->comment.length() != 0) {
- fprintf(to, "%s\n", this->comment.c_str());
- }
- WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL | OVERRIDE);
- fprintf(to, "%s %s", this->variable->type->QualifiedName().c_str(),
- this->variable->name.c_str());
- if (this->value.length() != 0) {
- fprintf(to, " = %s", this->value.c_str());
- }
- fprintf(to, ";\n");
-}
-
-Expression::~Expression()
-{
-}
-
-LiteralExpression::LiteralExpression(const string& v)
- :value(v)
-{
-}
-
-LiteralExpression::~LiteralExpression()
-{
-}
-
-void
-LiteralExpression::Write(FILE* to)
-{
- fprintf(to, "%s", this->value.c_str());
-}
-
-StringLiteralExpression::StringLiteralExpression(const string& v)
- :value(v)
-{
-}
-
-StringLiteralExpression::~StringLiteralExpression()
-{
-}
-
-void
-StringLiteralExpression::Write(FILE* to)
-{
- fprintf(to, "\"%s\"", this->value.c_str());
-}
-
-Variable::Variable()
- :type(NULL),
- name(),
- dimension(0)
-{
-}
-
-Variable::Variable(Type* t, const string& n)
- :type(t),
- name(n),
- dimension(0)
-{
-}
-
-Variable::Variable(Type* t, const string& n, int d)
- :type(t),
- name(n),
- dimension(d)
-{
-}
-
-Variable::~Variable()
-{
-}
-
-void
-Variable::GatherTypes(set<Type*>* types) const
-{
- types->insert(this->type);
-}
-
-void
-Variable::WriteDeclaration(FILE* to)
-{
- string dim;
- for (int i=0; i<this->dimension; i++) {
- dim += "[]";
- }
- fprintf(to, "%s%s %s", this->type->QualifiedName().c_str(), dim.c_str(),
- this->name.c_str());
-}
-
-void
-Variable::Write(FILE* to)
-{
- fprintf(to, "%s", name.c_str());
-}
-
-FieldVariable::FieldVariable(Expression* o, const string& n)
- :object(o),
- clazz(NULL),
- name(n)
-{
-}
-
-FieldVariable::FieldVariable(Type* c, const string& n)
- :object(NULL),
- clazz(c),
- name(n)
-{
-}
-
-FieldVariable::~FieldVariable()
-{
-}
-
-void
-FieldVariable::Write(FILE* to)
-{
- if (this->object != NULL) {
- this->object->Write(to);
- }
- else if (this->clazz != NULL) {
- fprintf(to, "%s", this->clazz->QualifiedName().c_str());
- }
- fprintf(to, ".%s", name.c_str());
-}
-
-
-Statement::~Statement()
-{
-}
-
-StatementBlock::StatementBlock()
-{
-}
-
-StatementBlock::~StatementBlock()
-{
-}
-
-void
-StatementBlock::Write(FILE* to)
-{
- fprintf(to, "{\n");
- int N = this->statements.size();
- for (int i=0; i<N; i++) {
- this->statements[i]->Write(to);
- }
- fprintf(to, "}\n");
-}
-
-void
-StatementBlock::Add(Statement* statement)
-{
- this->statements.push_back(statement);
-}
-
-void
-StatementBlock::Add(Expression* expression)
-{
- this->statements.push_back(new ExpressionStatement(expression));
-}
-
-ExpressionStatement::ExpressionStatement(Expression* e)
- :expression(e)
-{
-}
-
-ExpressionStatement::~ExpressionStatement()
-{
-}
-
-void
-ExpressionStatement::Write(FILE* to)
-{
- this->expression->Write(to);
- fprintf(to, ";\n");
-}
-
-Assignment::Assignment(Variable* l, Expression* r)
- :lvalue(l),
- rvalue(r),
- cast(NULL)
-{
-}
-
-Assignment::Assignment(Variable* l, Expression* r, Type* c)
- :lvalue(l),
- rvalue(r),
- cast(c)
-{
-}
-
-Assignment::~Assignment()
-{
-}
-
-void
-Assignment::Write(FILE* to)
-{
- this->lvalue->Write(to);
- fprintf(to, " = ");
- if (this->cast != NULL) {
- fprintf(to, "(%s)", this->cast->QualifiedName().c_str());
- }
- this->rvalue->Write(to);
-}
-
-MethodCall::MethodCall(const string& n)
- :obj(NULL),
- clazz(NULL),
- name(n)
-{
-}
-
-MethodCall::MethodCall(const string& n, int argc = 0, ...)
- :obj(NULL),
- clazz(NULL),
- name(n)
-{
- va_list args;
- va_start(args, argc);
- init(argc, args);
- va_end(args);
-}
-
-MethodCall::MethodCall(Expression* o, const string& n)
- :obj(o),
- clazz(NULL),
- name(n)
-{
-}
-
-MethodCall::MethodCall(Type* t, const string& n)
- :obj(NULL),
- clazz(t),
- name(n)
-{
-}
-
-MethodCall::MethodCall(Expression* o, const string& n, int argc = 0, ...)
- :obj(o),
- clazz(NULL),
- name(n)
-{
- va_list args;
- va_start(args, argc);
- init(argc, args);
- va_end(args);
-}
-
-MethodCall::MethodCall(Type* t, const string& n, int argc = 0, ...)
- :obj(NULL),
- clazz(t),
- name(n)
-{
- va_list args;
- va_start(args, argc);
- init(argc, args);
- va_end(args);
-}
-
-MethodCall::~MethodCall()
-{
-}
-
-void
-MethodCall::init(int n, va_list args)
-{
- for (int i=0; i<n; i++) {
- Expression* expression = (Expression*)va_arg(args, void*);
- this->arguments.push_back(expression);
- }
-}
-
-void
-MethodCall::Write(FILE* to)
-{
- if (this->obj != NULL) {
- this->obj->Write(to);
- fprintf(to, ".");
- }
- else if (this->clazz != NULL) {
- fprintf(to, "%s.", this->clazz->QualifiedName().c_str());
- }
- fprintf(to, "%s(", this->name.c_str());
- WriteArgumentList(to, this->arguments);
- fprintf(to, ")");
-}
-
-Comparison::Comparison(Expression* l, const string& o, Expression* r)
- :lvalue(l),
- op(o),
- rvalue(r)
-{
-}
-
-Comparison::~Comparison()
-{
-}
-
-void
-Comparison::Write(FILE* to)
-{
- fprintf(to, "(");
- this->lvalue->Write(to);
- fprintf(to, "%s", this->op.c_str());
- this->rvalue->Write(to);
- fprintf(to, ")");
-}
-
-NewExpression::NewExpression(Type* t)
- :type(t)
-{
-}
-
-NewExpression::NewExpression(Type* t, int argc = 0, ...)
- :type(t)
-{
- va_list args;
- va_start(args, argc);
- init(argc, args);
- va_end(args);
-}
-
-NewExpression::~NewExpression()
-{
-}
-
-void
-NewExpression::init(int n, va_list args)
-{
- for (int i=0; i<n; i++) {
- Expression* expression = (Expression*)va_arg(args, void*);
- this->arguments.push_back(expression);
- }
-}
-
-void
-NewExpression::Write(FILE* to)
-{
- fprintf(to, "new %s(", this->type->InstantiableName().c_str());
- WriteArgumentList(to, this->arguments);
- fprintf(to, ")");
-}
-
-NewArrayExpression::NewArrayExpression(Type* t, Expression* s)
- :type(t),
- size(s)
-{
-}
-
-NewArrayExpression::~NewArrayExpression()
-{
-}
-
-void
-NewArrayExpression::Write(FILE* to)
-{
- fprintf(to, "new %s[", this->type->QualifiedName().c_str());
- size->Write(to);
- fprintf(to, "]");
-}
-
-Ternary::Ternary()
- :condition(NULL),
- ifpart(NULL),
- elsepart(NULL)
-{
-}
-
-Ternary::Ternary(Expression* a, Expression* b, Expression* c)
- :condition(a),
- ifpart(b),
- elsepart(c)
-{
-}
-
-Ternary::~Ternary()
-{
-}
-
-void
-Ternary::Write(FILE* to)
-{
- fprintf(to, "((");
- this->condition->Write(to);
- fprintf(to, ")?(");
- this->ifpart->Write(to);
- fprintf(to, "):(");
- this->elsepart->Write(to);
- fprintf(to, "))");
-}
-
-Cast::Cast()
- :type(NULL),
- expression(NULL)
-{
-}
-
-Cast::Cast(Type* t, Expression* e)
- :type(t),
- expression(e)
-{
-}
-
-Cast::~Cast()
-{
-}
-
-void
-Cast::Write(FILE* to)
-{
- fprintf(to, "((%s)", this->type->QualifiedName().c_str());
- expression->Write(to);
- fprintf(to, ")");
-}
-
-VariableDeclaration::VariableDeclaration(Variable* l, Expression* r, Type* c)
- :lvalue(l),
- cast(c),
- rvalue(r)
-{
-}
-
-VariableDeclaration::VariableDeclaration(Variable* l)
- :lvalue(l),
- cast(NULL),
- rvalue(NULL)
-{
-}
-
-VariableDeclaration::~VariableDeclaration()
-{
-}
-
-void
-VariableDeclaration::Write(FILE* to)
-{
- this->lvalue->WriteDeclaration(to);
- if (this->rvalue != NULL) {
- fprintf(to, " = ");
- if (this->cast != NULL) {
- fprintf(to, "(%s)", this->cast->QualifiedName().c_str());
- }
- this->rvalue->Write(to);
- }
- fprintf(to, ";\n");
-}
-
-IfStatement::IfStatement()
- :expression(NULL),
- statements(new StatementBlock),
- elseif(NULL)
-{
-}
-
-IfStatement::~IfStatement()
-{
-}
-
-void
-IfStatement::Write(FILE* to)
-{
- if (this->expression != NULL) {
- fprintf(to, "if (");
- this->expression->Write(to);
- fprintf(to, ") ");
- }
- this->statements->Write(to);
- if (this->elseif != NULL) {
- fprintf(to, "else ");
- this->elseif->Write(to);
- }
-}
-
-ReturnStatement::ReturnStatement(Expression* e)
- :expression(e)
-{
-}
-
-ReturnStatement::~ReturnStatement()
-{
-}
-
-void
-ReturnStatement::Write(FILE* to)
-{
- fprintf(to, "return ");
- this->expression->Write(to);
- fprintf(to, ";\n");
-}
-
-TryStatement::TryStatement()
- :statements(new StatementBlock)
-{
-}
-
-TryStatement::~TryStatement()
-{
-}
-
-void
-TryStatement::Write(FILE* to)
-{
- fprintf(to, "try ");
- this->statements->Write(to);
-}
-
-CatchStatement::CatchStatement(Variable* e)
- :statements(new StatementBlock),
- exception(e)
-{
-}
-
-CatchStatement::~CatchStatement()
-{
-}
-
-void
-CatchStatement::Write(FILE* to)
-{
- fprintf(to, "catch ");
- if (this->exception != NULL) {
- fprintf(to, "(");
- this->exception->WriteDeclaration(to);
- fprintf(to, ") ");
- }
- this->statements->Write(to);
-}
-
-FinallyStatement::FinallyStatement()
- :statements(new StatementBlock)
-{
-}
-
-FinallyStatement::~FinallyStatement()
-{
-}
-
-void
-FinallyStatement::Write(FILE* to)
-{
- fprintf(to, "finally ");
- this->statements->Write(to);
-}
-
-Case::Case()
- :statements(new StatementBlock)
-{
-}
-
-Case::Case(const string& c)
- :statements(new StatementBlock)
-{
- cases.push_back(c);
-}
-
-Case::~Case()
-{
-}
-
-void
-Case::Write(FILE* to)
-{
- int N = this->cases.size();
- if (N > 0) {
- for (int i=0; i<N; i++) {
- string s = this->cases[i];
- if (s.length() != 0) {
- fprintf(to, "case %s:\n", s.c_str());
- } else {
- fprintf(to, "default:\n");
- }
- }
- } else {
- fprintf(to, "default:\n");
- }
- statements->Write(to);
-}
-
-SwitchStatement::SwitchStatement(Expression* e)
- :expression(e)
-{
-}
-
-SwitchStatement::~SwitchStatement()
-{
-}
-
-void
-SwitchStatement::Write(FILE* to)
-{
- fprintf(to, "switch (");
- this->expression->Write(to);
- fprintf(to, ")\n{\n");
- int N = this->cases.size();
- for (int i=0; i<N; i++) {
- this->cases[i]->Write(to);
- }
- fprintf(to, "}\n");
-}
-
-Break::Break()
-{
-}
-
-Break::~Break()
-{
-}
-
-void
-Break::Write(FILE* to)
-{
- fprintf(to, "break;\n");
-}
-
-Method::Method()
- :ClassElement(),
- modifiers(0),
- returnType(NULL), // (NULL means constructor)
- returnTypeDimension(0),
- statements(NULL)
-{
-}
-
-Method::~Method()
-{
-}
-
-void
-Method::GatherTypes(set<Type*>* types) const
-{
- size_t N, i;
-
- if (this->returnType) {
- types->insert(this->returnType);
- }
-
- N = this->parameters.size();
- for (i=0; i<N; i++) {
- this->parameters[i]->GatherTypes(types);
- }
-
- N = this->exceptions.size();
- for (i=0; i<N; i++) {
- types->insert(this->exceptions[i]);
- }
-}
-
-void
-Method::Write(FILE* to)
-{
- size_t N, i;
-
- if (this->comment.length() != 0) {
- fprintf(to, "%s\n", this->comment.c_str());
- }
-
- WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | ABSTRACT | FINAL | OVERRIDE);
-
- if (this->returnType != NULL) {
- string dim;
- for (i=0; i<this->returnTypeDimension; i++) {
- dim += "[]";
- }
- fprintf(to, "%s%s ", this->returnType->QualifiedName().c_str(),
- dim.c_str());
- }
-
- fprintf(to, "%s(", this->name.c_str());
-
- N = this->parameters.size();
- for (i=0; i<N; i++) {
- this->parameters[i]->WriteDeclaration(to);
- if (i != N-1) {
- fprintf(to, ", ");
- }
- }
-
- fprintf(to, ")");
-
- N = this->exceptions.size();
- for (i=0; i<N; i++) {
- if (i == 0) {
- fprintf(to, " throws ");
- } else {
- fprintf(to, ", ");
- }
- fprintf(to, "%s", this->exceptions[i]->QualifiedName().c_str());
- }
-
- if (this->statements == NULL) {
- fprintf(to, ";\n");
- } else {
- fprintf(to, "\n");
- this->statements->Write(to);
- }
-}
-
-Class::Class()
- :modifiers(0),
- what(CLASS),
- type(NULL),
- extends(NULL)
-{
-}
-
-Class::~Class()
-{
-}
-
-void
-Class::GatherTypes(set<Type*>* types) const
-{
- int N, i;
-
- types->insert(this->type);
- if (this->extends != NULL) {
- types->insert(this->extends);
- }
-
- N = this->interfaces.size();
- for (i=0; i<N; i++) {
- types->insert(this->interfaces[i]);
- }
-
- N = this->elements.size();
- for (i=0; i<N; i++) {
- this->elements[i]->GatherTypes(types);
- }
-}
-
-void
-Class::Write(FILE* to)
-{
- size_t N, i;
-
- if (this->comment.length() != 0) {
- fprintf(to, "%s\n", this->comment.c_str());
- }
-
- WriteModifiers(to, this->modifiers, ALL_MODIFIERS);
-
- if (this->what == Class::CLASS) {
- fprintf(to, "class ");
- } else {
- fprintf(to, "interface ");
- }
-
- string name = this->type->Name();
- size_t pos = name.rfind('.');
- if (pos != string::npos) {
- name = name.c_str() + pos + 1;
- }
-
- fprintf(to, "%s", name.c_str());
-
- if (this->extends != NULL) {
- fprintf(to, " extends %s", this->extends->QualifiedName().c_str());
- }
-
- N = this->interfaces.size();
- if (N != 0) {
- if (this->what == Class::CLASS) {
- fprintf(to, " implements");
- } else {
- fprintf(to, " extends");
- }
- for (i=0; i<N; i++) {
- fprintf(to, " %s", this->interfaces[i]->QualifiedName().c_str());
- }
- }
-
- fprintf(to, "\n");
- fprintf(to, "{\n");
-
- N = this->elements.size();
- for (i=0; i<N; i++) {
- this->elements[i]->Write(to);
- }
-
- fprintf(to, "}\n");
-
-}
-
-Document::Document()
-{
-}
-
-Document::~Document()
-{
-}
-
-static string
-escape_backslashes(const string& str)
-{
- string result;
- const size_t I=str.length();
- for (size_t i=0; i<I; i++) {
- char c = str[i];
- if (c == '\\') {
- result += "\\\\";
- } else {
- result += c;
- }
- }
- return result;
-}
-
-void
-Document::Write(FILE* to)
-{
- size_t N, i;
-
- if (this->comment.length() != 0) {
- fprintf(to, "%s\n", this->comment.c_str());
- }
- fprintf(to, "/*\n"
- " * This file is auto-generated. DO NOT MODIFY.\n"
- " * Original file: %s\n"
- " */\n", escape_backslashes(this->originalSrc).c_str());
- if (this->package.length() != 0) {
- fprintf(to, "package %s;\n", this->package.c_str());
- }
-
- N = this->classes.size();
- for (i=0; i<N; i++) {
- Class* c = this->classes[i];
- c->Write(to);
- }
-}
-
diff --git a/tools/aidl/AST.h b/tools/aidl/AST.h
deleted file mode 100644
index d7bfccb..0000000
--- a/tools/aidl/AST.h
+++ /dev/null
@@ -1,371 +0,0 @@
-#ifndef AIDL_AST_H_
-#define AIDL_AST_H_
-
-#include <string>
-#include <vector>
-#include <set>
-#include <stdarg.h>
-#include <stdio.h>
-
-using namespace std;
-
-class Type;
-
-enum {
- PACKAGE_PRIVATE = 0x00000000,
- PUBLIC = 0x00000001,
- PRIVATE = 0x00000002,
- PROTECTED = 0x00000003,
- SCOPE_MASK = 0x00000003,
-
- STATIC = 0x00000010,
- FINAL = 0x00000020,
- ABSTRACT = 0x00000040,
-
- OVERRIDE = 0x00000100,
-
- ALL_MODIFIERS = 0xffffffff
-};
-
-// Write the modifiers that are set in both mod and mask
-void WriteModifiers(FILE* to, int mod, int mask);
-
-struct ClassElement
-{
- ClassElement();
- virtual ~ClassElement();
-
- virtual void GatherTypes(set<Type*>* types) const = 0;
- virtual void Write(FILE* to) = 0;
-};
-
-struct Expression
-{
- virtual ~Expression();
- virtual void Write(FILE* to) = 0;
-};
-
-struct LiteralExpression : public Expression
-{
- string value;
-
- LiteralExpression(const string& value);
- virtual ~LiteralExpression();
- virtual void Write(FILE* to);
-};
-
-// TODO: also escape the contents. not needed for now
-struct StringLiteralExpression : public Expression
-{
- string value;
-
- StringLiteralExpression(const string& value);
- virtual ~StringLiteralExpression();
- virtual void Write(FILE* to);
-};
-
-struct Variable : public Expression
-{
- Type* type;
- string name;
- int dimension;
-
- Variable();
- Variable(Type* type, const string& name);
- Variable(Type* type, const string& name, int dimension);
- virtual ~Variable();
-
- virtual void GatherTypes(set<Type*>* types) const;
- void WriteDeclaration(FILE* to);
- void Write(FILE* to);
-};
-
-struct FieldVariable : public Expression
-{
- Expression* object;
- Type* clazz;
- string name;
-
- FieldVariable(Expression* object, const string& name);
- FieldVariable(Type* clazz, const string& name);
- virtual ~FieldVariable();
-
- void Write(FILE* to);
-};
-
-struct Field : public ClassElement
-{
- string comment;
- int modifiers;
- Variable *variable;
- string value;
-
- Field();
- Field(int modifiers, Variable* variable);
- virtual ~Field();
-
- virtual void GatherTypes(set<Type*>* types) const;
- virtual void Write(FILE* to);
-};
-
-struct Statement
-{
- virtual ~Statement();
- virtual void Write(FILE* to) = 0;
-};
-
-struct StatementBlock : public Statement
-{
- vector<Statement*> statements;
-
- StatementBlock();
- virtual ~StatementBlock();
- virtual void Write(FILE* to);
-
- void Add(Statement* statement);
- void Add(Expression* expression);
-};
-
-struct ExpressionStatement : public Statement
-{
- Expression* expression;
-
- ExpressionStatement(Expression* expression);
- virtual ~ExpressionStatement();
- virtual void Write(FILE* to);
-};
-
-struct Assignment : public Expression
-{
- Variable* lvalue;
- Expression* rvalue;
- Type* cast;
-
- Assignment(Variable* lvalue, Expression* rvalue);
- Assignment(Variable* lvalue, Expression* rvalue, Type* cast);
- virtual ~Assignment();
- virtual void Write(FILE* to);
-};
-
-struct MethodCall : public Expression
-{
- Expression* obj;
- Type* clazz;
- string name;
- vector<Expression*> arguments;
- vector<string> exceptions;
-
- MethodCall(const string& name);
- MethodCall(const string& name, int argc, ...);
- MethodCall(Expression* obj, const string& name);
- MethodCall(Type* clazz, const string& name);
- MethodCall(Expression* obj, const string& name, int argc, ...);
- MethodCall(Type* clazz, const string& name, int argc, ...);
- virtual ~MethodCall();
- virtual void Write(FILE* to);
-
-private:
- void init(int n, va_list args);
-};
-
-struct Comparison : public Expression
-{
- Expression* lvalue;
- string op;
- Expression* rvalue;
-
- Comparison(Expression* lvalue, const string& op, Expression* rvalue);
- virtual ~Comparison();
- virtual void Write(FILE* to);
-};
-
-struct NewExpression : public Expression
-{
- Type* type;
- vector<Expression*> arguments;
-
- NewExpression(Type* type);
- NewExpression(Type* type, int argc, ...);
- virtual ~NewExpression();
- virtual void Write(FILE* to);
-
-private:
- void init(int n, va_list args);
-};
-
-struct NewArrayExpression : public Expression
-{
- Type* type;
- Expression* size;
-
- NewArrayExpression(Type* type, Expression* size);
- virtual ~NewArrayExpression();
- virtual void Write(FILE* to);
-};
-
-struct Ternary : public Expression
-{
- Expression* condition;
- Expression* ifpart;
- Expression* elsepart;
-
- Ternary();
- Ternary(Expression* condition, Expression* ifpart, Expression* elsepart);
- virtual ~Ternary();
- virtual void Write(FILE* to);
-};
-
-struct Cast : public Expression
-{
- Type* type;
- Expression* expression;
-
- Cast();
- Cast(Type* type, Expression* expression);
- virtual ~Cast();
- virtual void Write(FILE* to);
-};
-
-struct VariableDeclaration : public Statement
-{
- Variable* lvalue;
- Type* cast;
- Expression* rvalue;
-
- VariableDeclaration(Variable* lvalue);
- VariableDeclaration(Variable* lvalue, Expression* rvalue, Type* cast = NULL);
- virtual ~VariableDeclaration();
- virtual void Write(FILE* to);
-};
-
-struct IfStatement : public Statement
-{
- Expression* expression;
- StatementBlock* statements;
- IfStatement* elseif;
-
- IfStatement();
- virtual ~IfStatement();
- virtual void Write(FILE* to);
-};
-
-struct ReturnStatement : public Statement
-{
- Expression* expression;
-
- ReturnStatement(Expression* expression);
- virtual ~ReturnStatement();
- virtual void Write(FILE* to);
-};
-
-struct TryStatement : public Statement
-{
- StatementBlock* statements;
-
- TryStatement();
- virtual ~TryStatement();
- virtual void Write(FILE* to);
-};
-
-struct CatchStatement : public Statement
-{
- StatementBlock* statements;
- Variable* exception;
-
- CatchStatement(Variable* exception);
- virtual ~CatchStatement();
- virtual void Write(FILE* to);
-};
-
-struct FinallyStatement : public Statement
-{
- StatementBlock* statements;
-
- FinallyStatement();
- virtual ~FinallyStatement();
- virtual void Write(FILE* to);
-};
-
-struct Case
-{
- vector<string> cases;
- StatementBlock* statements;
-
- Case();
- Case(const string& c);
- virtual ~Case();
- virtual void Write(FILE* to);
-};
-
-struct SwitchStatement : public Statement
-{
- Expression* expression;
- vector<Case*> cases;
-
- SwitchStatement(Expression* expression);
- virtual ~SwitchStatement();
- virtual void Write(FILE* to);
-};
-
-struct Break : public Statement
-{
- Break();
- virtual ~Break();
- virtual void Write(FILE* to);
-};
-
-struct Method : public ClassElement
-{
- string comment;
- int modifiers;
- Type* returnType;
- size_t returnTypeDimension;
- string name;
- vector<Variable*> parameters;
- vector<Type*> exceptions;
- StatementBlock* statements;
-
- Method();
- virtual ~Method();
-
- virtual void GatherTypes(set<Type*>* types) const;
- virtual void Write(FILE* to);
-};
-
-struct Class : public ClassElement
-{
- enum {
- CLASS,
- INTERFACE
- };
-
- string comment;
- int modifiers;
- int what; // CLASS or INTERFACE
- Type* type;
- Type* extends;
- vector<Type*> interfaces;
- vector<ClassElement*> elements;
-
- Class();
- virtual ~Class();
-
- virtual void GatherTypes(set<Type*>* types) const;
- virtual void Write(FILE* to);
-};
-
-struct Document
-{
- string comment;
- string package;
- string originalSrc;
- set<Type*> imports;
- vector<Class*> classes;
-
- Document();
- virtual ~Document();
-
- virtual void Write(FILE* to);
-};
-
-#endif // AIDL_AST_H_
diff --git a/tools/aidl/Android.mk b/tools/aidl/Android.mk
deleted file mode 100644
index d11264e..0000000
--- a/tools/aidl/Android.mk
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright 2007 The Android Open Source Project
-#
-# Copies files into the directory structure described by a manifest
-
-# This tool is prebuilt if we're doing an app-only build.
-ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
-
-LOCAL_PATH:= $(call my-dir)
-
-# Logic shared between aidl and its unittests
-include $(CLEAR_VARS)
-LOCAL_MODULE := libaidl-common
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_CLANG_CFLAGS := -Wall -Werror
-# Tragically, the code is riddled with unused parameters.
-LOCAL_CLANG_CFLAGS += -Wno-unused-parameter
-# yacc dumps a lot of code *just in case*.
-LOCAL_CLANG_CFLAGS += -Wno-unused-function
-LOCAL_CLANG_CFLAGS += -Wno-unneeded-internal-declaration
-# yacc is a tool from a more civilized age.
-LOCAL_CLANG_CFLAGS += -Wno-deprecated-register
-# yacc also has a habit of using char* over const char*.
-LOCAL_CLANG_CFLAGS += -Wno-writable-strings
-
-LOCAL_SRC_FILES := \
- AST.cpp \
- Type.cpp \
- aidl.cpp \
- aidl_language.cpp \
- aidl_language_l.l \
- aidl_language_y.y \
- generate_java.cpp \
- generate_java_binder.cpp \
- options.cpp \
- search_path.cpp \
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-# aidl executable
-include $(CLEAR_VARS)
-LOCAL_MODULE := aidl
-
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SRC_FILES := main.cpp
-LOCAL_STATIC_LIBRARIES := libaidl-common
-include $(BUILD_HOST_EXECUTABLE)
-
-
-# TODO(wiley) Compile these for mac as well after b/22771504
-ifeq ($(HOST_OS),linux)
-# Unit tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := aidl_unittests
-
-LOCAL_CFLAGS := -g -DUNIT_TEST -Wall -Werror
-# Tragically, the code is riddled with unused parameters.
-LOCAL_CLANG_CFLAGS := -Wno-unused-parameter
-LOCAL_SRC_FILES := \
- options_unittest.cpp \
- test_main.cpp \
- tests/end_to_end_tests.cpp \
- tests/example_interface_test_data.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- libchrome-host \
-
-LOCAL_STATIC_LIBRARIES := \
- libaidl-common \
- libgmock_host \
- libgtest_host \
-
-LOCAL_LDLIBS_linux := -lrt
-
-include $(BUILD_HOST_NATIVE_TEST)
-endif # HOST_OS == linux
-
-endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
diff --git a/tools/aidl/NOTICE b/tools/aidl/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/tools/aidl/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2005-2008, 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.
-
- 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.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
diff --git a/tools/aidl/Type.cpp b/tools/aidl/Type.cpp
deleted file mode 100644
index d9b8b88..0000000
--- a/tools/aidl/Type.cpp
+++ /dev/null
@@ -1,1245 +0,0 @@
-#include "Type.h"
-
-#include <sys/types.h>
-
-Namespace NAMES;
-
-Type* VOID_TYPE;
-Type* BOOLEAN_TYPE;
-Type* BYTE_TYPE;
-Type* CHAR_TYPE;
-Type* INT_TYPE;
-Type* LONG_TYPE;
-Type* FLOAT_TYPE;
-Type* DOUBLE_TYPE;
-Type* STRING_TYPE;
-Type* OBJECT_TYPE;
-Type* CHAR_SEQUENCE_TYPE;
-Type* TEXT_UTILS_TYPE;
-Type* REMOTE_EXCEPTION_TYPE;
-Type* RUNTIME_EXCEPTION_TYPE;
-Type* IBINDER_TYPE;
-Type* IINTERFACE_TYPE;
-Type* BINDER_NATIVE_TYPE;
-Type* BINDER_PROXY_TYPE;
-Type* PARCEL_TYPE;
-Type* PARCELABLE_INTERFACE_TYPE;
-Type* CONTEXT_TYPE;
-Type* MAP_TYPE;
-Type* LIST_TYPE;
-Type* CLASSLOADER_TYPE;
-
-Expression* NULL_VALUE;
-Expression* THIS_VALUE;
-Expression* SUPER_VALUE;
-Expression* TRUE_VALUE;
-Expression* FALSE_VALUE;
-
-void
-register_base_types()
-{
- VOID_TYPE = new BasicType("void",
- "XXX", "XXX", "XXX", "XXX", "XXX");
- NAMES.Add(VOID_TYPE);
-
- BOOLEAN_TYPE = new BooleanType();
- NAMES.Add(BOOLEAN_TYPE);
-
- BYTE_TYPE = new BasicType("byte",
- "writeByte", "readByte", "writeByteArray", "createByteArray",
- "readByteArray");
- NAMES.Add(BYTE_TYPE);
-
- CHAR_TYPE = new CharType();
- NAMES.Add(CHAR_TYPE);
-
- INT_TYPE = new BasicType("int",
- "writeInt", "readInt", "writeIntArray", "createIntArray",
- "readIntArray");
- NAMES.Add(INT_TYPE);
-
- LONG_TYPE = new BasicType("long",
- "writeLong", "readLong", "writeLongArray", "createLongArray",
- "readLongArray");
- NAMES.Add(LONG_TYPE);
-
- FLOAT_TYPE = new BasicType("float",
- "writeFloat", "readFloat", "writeFloatArray", "createFloatArray",
- "readFloatArray");
- NAMES.Add(FLOAT_TYPE);
-
- DOUBLE_TYPE = new BasicType("double",
- "writeDouble", "readDouble", "writeDoubleArray",
- "createDoubleArray", "readDoubleArray");
- NAMES.Add(DOUBLE_TYPE);
-
- STRING_TYPE = new StringType();
- NAMES.Add(STRING_TYPE);
-
- OBJECT_TYPE = new Type("java.lang", "Object", Type::BUILT_IN, false, false);
- NAMES.Add(OBJECT_TYPE);
-
- CHAR_SEQUENCE_TYPE = new CharSequenceType();
- NAMES.Add(CHAR_SEQUENCE_TYPE);
-
- MAP_TYPE = new MapType();
- NAMES.Add(MAP_TYPE);
-
- LIST_TYPE = new ListType();
- NAMES.Add(LIST_TYPE);
-
- TEXT_UTILS_TYPE = new Type("android.text", "TextUtils", Type::BUILT_IN, false, false);
- NAMES.Add(TEXT_UTILS_TYPE);
-
- REMOTE_EXCEPTION_TYPE = new RemoteExceptionType();
- NAMES.Add(REMOTE_EXCEPTION_TYPE);
-
- RUNTIME_EXCEPTION_TYPE = new RuntimeExceptionType();
- NAMES.Add(RUNTIME_EXCEPTION_TYPE);
-
- IBINDER_TYPE = new IBinderType();
- NAMES.Add(IBINDER_TYPE);
-
- IINTERFACE_TYPE = new IInterfaceType();
- NAMES.Add(IINTERFACE_TYPE);
-
- BINDER_NATIVE_TYPE = new BinderType();
- NAMES.Add(BINDER_NATIVE_TYPE);
-
- BINDER_PROXY_TYPE = new BinderProxyType();
- NAMES.Add(BINDER_PROXY_TYPE);
-
- PARCEL_TYPE = new ParcelType();
- NAMES.Add(PARCEL_TYPE);
-
- PARCELABLE_INTERFACE_TYPE = new ParcelableInterfaceType();
- NAMES.Add(PARCELABLE_INTERFACE_TYPE);
-
- CONTEXT_TYPE = new Type("android.content", "Context", Type::BUILT_IN, false, false);
- NAMES.Add(CONTEXT_TYPE);
-
- CLASSLOADER_TYPE = new ClassLoaderType();
- NAMES.Add(CLASSLOADER_TYPE);
-
- NULL_VALUE = new LiteralExpression("null");
- THIS_VALUE = new LiteralExpression("this");
- SUPER_VALUE = new LiteralExpression("super");
- TRUE_VALUE = new LiteralExpression("true");
- FALSE_VALUE = new LiteralExpression("false");
-
- NAMES.AddGenericType("java.util", "List", 1);
- NAMES.AddGenericType("java.util", "Map", 2);
-}
-
-static Type*
-make_generic_type(const string& package, const string& name,
- const vector<Type*>& args)
-{
- if (package == "java.util" && name == "List") {
- return new GenericListType("java.util", "List", args);
- }
- return NULL;
- //return new GenericType(package, name, args);
-}
-
-// ================================================================
-
-Type::Type(const string& name, int kind, bool canWriteToParcel, bool canBeOut)
- :m_package(),
- m_name(name),
- m_declFile(""),
- m_declLine(-1),
- m_kind(kind),
- m_canWriteToParcel(canWriteToParcel),
- m_canBeOut(canBeOut)
-{
- m_qualifiedName = name;
-}
-
-Type::Type(const string& package, const string& name,
- int kind, bool canWriteToParcel, bool canBeOut,
- const string& declFile, int declLine)
- :m_package(package),
- m_name(name),
- m_declFile(declFile),
- m_declLine(declLine),
- m_kind(kind),
- m_canWriteToParcel(canWriteToParcel),
- m_canBeOut(canBeOut)
-{
- if (package.length() > 0) {
- m_qualifiedName = package;
- m_qualifiedName += '.';
- }
- m_qualifiedName += name;
-}
-
-Type::~Type()
-{
-}
-
-bool
-Type::CanBeArray() const
-{
- return false;
-}
-
-string
-Type::ImportType() const
-{
- return m_qualifiedName;
-}
-
-string
-Type::CreatorName() const
-{
- return "";
-}
-
-string
-Type::InstantiableName() const
-{
- return QualifiedName();
-}
-
-
-void
-Type::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%sn",
- __FILE__, __LINE__, m_qualifiedName.c_str());
- addTo->Add(new LiteralExpression("/* WriteToParcel error "
- + m_qualifiedName + " */"));
-}
-
-void
-Type::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
- __FILE__, __LINE__, m_qualifiedName.c_str());
- addTo->Add(new LiteralExpression("/* CreateFromParcel error "
- + m_qualifiedName + " */"));
-}
-
-void
-Type::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
- __FILE__, __LINE__, m_qualifiedName.c_str());
- addTo->Add(new LiteralExpression("/* ReadFromParcel error "
- + m_qualifiedName + " */"));
-}
-
-void
-Type::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
- __FILE__, __LINE__, m_qualifiedName.c_str());
- addTo->Add(new LiteralExpression("/* WriteArrayToParcel error "
- + m_qualifiedName + " */"));
-}
-
-void
-Type::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
- __FILE__, __LINE__, m_qualifiedName.c_str());
- addTo->Add(new LiteralExpression("/* CreateArrayFromParcel error "
- + m_qualifiedName + " */"));
-}
-
-void
-Type::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
- __FILE__, __LINE__, m_qualifiedName.c_str());
- addTo->Add(new LiteralExpression("/* ReadArrayFromParcel error "
- + m_qualifiedName + " */"));
-}
-
-void
-Type::SetQualifiedName(const string& qualified)
-{
- m_qualifiedName = qualified;
-}
-
-Expression*
-Type::BuildWriteToParcelFlags(int flags)
-{
- if (flags == 0) {
- return new LiteralExpression("0");
- }
- if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0) {
- return new FieldVariable(PARCELABLE_INTERFACE_TYPE,
- "PARCELABLE_WRITE_RETURN_VALUE");
- }
- return new LiteralExpression("0");
-}
-
-// ================================================================
-
-BasicType::BasicType(const string& name, const string& marshallParcel,
- const string& unmarshallParcel, const string& writeArrayParcel,
- const string& createArrayParcel, const string& readArrayParcel)
- :Type(name, BUILT_IN, true, false),
- m_marshallParcel(marshallParcel),
- m_unmarshallParcel(unmarshallParcel),
- m_writeArrayParcel(writeArrayParcel),
- m_createArrayParcel(createArrayParcel),
- m_readArrayParcel(readArrayParcel)
-{
-}
-
-void
-BasicType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, m_marshallParcel, 1, v));
-}
-
-void
-BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(parcel, m_unmarshallParcel)));
-}
-
-bool
-BasicType::CanBeArray() const
-{
- return true;
-}
-
-void
-BasicType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, m_writeArrayParcel, 1, v));
-}
-
-void
-BasicType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(parcel, m_createArrayParcel)));
-}
-
-void
-BasicType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new MethodCall(parcel, m_readArrayParcel, 1, v));
-}
-
-// ================================================================
-
-BooleanType::BooleanType()
- :Type("boolean", BUILT_IN, true, false)
-{
-}
-
-void
-BooleanType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeInt", 1,
- new Ternary(v, new LiteralExpression("1"),
- new LiteralExpression("0"))));
-}
-
-void
-BooleanType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new Comparison(new LiteralExpression("0"),
- "!=", new MethodCall(parcel, "readInt"))));
-}
-
-bool
-BooleanType::CanBeArray() const
-{
- return true;
-}
-
-void
-BooleanType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeBooleanArray", 1, v));
-}
-
-void
-BooleanType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(parcel, "createBooleanArray")));
-}
-
-void
-BooleanType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new MethodCall(parcel, "readBooleanArray", 1, v));
-}
-
-// ================================================================
-
-CharType::CharType()
- :Type("char", BUILT_IN, true, false)
-{
-}
-
-void
-CharType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeInt", 1,
- new Cast(INT_TYPE, v)));
-}
-
-void
-CharType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(parcel, "readInt"), this));
-}
-
-bool
-CharType::CanBeArray() const
-{
- return true;
-}
-
-void
-CharType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeCharArray", 1, v));
-}
-
-void
-CharType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(parcel, "createCharArray")));
-}
-
-void
-CharType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new MethodCall(parcel, "readCharArray", 1, v));
-}
-
-// ================================================================
-
-StringType::StringType()
- :Type("java.lang", "String", BUILT_IN, true, false)
-{
-}
-
-string
-StringType::CreatorName() const
-{
- return "android.os.Parcel.STRING_CREATOR";
-}
-
-void
-StringType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeString", 1, v));
-}
-
-void
-StringType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(parcel, "readString")));
-}
-
-bool
-StringType::CanBeArray() const
-{
- return true;
-}
-
-void
-StringType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeStringArray", 1, v));
-}
-
-void
-StringType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(parcel, "createStringArray")));
-}
-
-void
-StringType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new MethodCall(parcel, "readStringArray", 1, v));
-}
-
-// ================================================================
-
-CharSequenceType::CharSequenceType()
- :Type("java.lang", "CharSequence", BUILT_IN, true, false)
-{
-}
-
-string
-CharSequenceType::CreatorName() const
-{
- return "android.os.Parcel.STRING_CREATOR";
-}
-
-void
-CharSequenceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- // if (v != null) {
- // parcel.writeInt(1);
- // v.writeToParcel(parcel);
- // } else {
- // parcel.writeInt(0);
- // }
- IfStatement* elsepart = new IfStatement();
- elsepart->statements->Add(new MethodCall(parcel, "writeInt", 1,
- new LiteralExpression("0")));
- IfStatement* ifpart = new IfStatement;
- ifpart->expression = new Comparison(v, "!=", NULL_VALUE);
- ifpart->elseif = elsepart;
- ifpart->statements->Add(new MethodCall(parcel, "writeInt", 1,
- new LiteralExpression("1")));
- ifpart->statements->Add(new MethodCall(TEXT_UTILS_TYPE, "writeToParcel",
- 3, v, parcel, BuildWriteToParcelFlags(flags)));
-
- addTo->Add(ifpart);
-}
-
-void
-CharSequenceType::CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- // if (0 != parcel.readInt()) {
- // v = TextUtils.createFromParcel(parcel)
- // } else {
- // v = null;
- // }
- IfStatement* elsepart = new IfStatement();
- elsepart->statements->Add(new Assignment(v, NULL_VALUE));
-
- IfStatement* ifpart = new IfStatement();
- ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
- new MethodCall(parcel, "readInt"));
- ifpart->elseif = elsepart;
- ifpart->statements->Add(new Assignment(v,
- new MethodCall(TEXT_UTILS_TYPE,
- "CHAR_SEQUENCE_CREATOR.createFromParcel", 1, parcel)));
-
- addTo->Add(ifpart);
-}
-
-
-// ================================================================
-
-RemoteExceptionType::RemoteExceptionType()
- :Type("android.os", "RemoteException", BUILT_IN, false, false)
-{
-}
-
-void
-RemoteExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-void
-RemoteExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-// ================================================================
-
-RuntimeExceptionType::RuntimeExceptionType()
- :Type("java.lang", "RuntimeException", BUILT_IN, false, false)
-{
-}
-
-void
-RuntimeExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-void
-RuntimeExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-
-// ================================================================
-
-IBinderType::IBinderType()
- :Type("android.os", "IBinder", BUILT_IN, true, false)
-{
-}
-
-void
-IBinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeStrongBinder", 1, v));
-}
-
-void
-IBinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(parcel, "readStrongBinder")));
-}
-
-void
-IBinderType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeBinderArray", 1, v));
-}
-
-void
-IBinderType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(parcel, "createBinderArray")));
-}
-
-void
-IBinderType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- addTo->Add(new MethodCall(parcel, "readBinderArray", 1, v));
-}
-
-
-// ================================================================
-
-IInterfaceType::IInterfaceType()
- :Type("android.os", "IInterface", BUILT_IN, false, false)
-{
-}
-
-void
-IInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-void
-IInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-
-// ================================================================
-
-BinderType::BinderType()
- :Type("android.os", "Binder", BUILT_IN, false, false)
-{
-}
-
-void
-BinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-void
-BinderType::CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-
-// ================================================================
-
-BinderProxyType::BinderProxyType()
- :Type("android.os", "BinderProxy", BUILT_IN, false, false)
-{
-}
-
-void
-BinderProxyType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-void
-BinderProxyType::CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-
-// ================================================================
-
-ParcelType::ParcelType()
- :Type("android.os", "Parcel", BUILT_IN, false, false)
-{
-}
-
-void
-ParcelType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-void
-ParcelType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-// ================================================================
-
-ParcelableInterfaceType::ParcelableInterfaceType()
- :Type("android.os", "Parcelable", BUILT_IN, false, false)
-{
-}
-
-void
-ParcelableInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-void
-ParcelableInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
-}
-
-// ================================================================
-
-MapType::MapType()
- :Type("java.util", "Map", BUILT_IN, true, true)
-{
-}
-
-void
-MapType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeMap", 1, v));
-}
-
-static void EnsureClassLoader(StatementBlock* addTo, Variable** cl)
-{
- // We don't want to look up the class loader once for every
- // collection argument, so ensure we do it at most once per method.
- if (*cl == NULL) {
- *cl = new Variable(CLASSLOADER_TYPE, "cl");
- addTo->Add(new VariableDeclaration(*cl,
- new LiteralExpression("this.getClass().getClassLoader()"),
- CLASSLOADER_TYPE));
- }
-}
-
-void
-MapType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl)
-{
- EnsureClassLoader(addTo, cl);
- addTo->Add(new Assignment(v, new MethodCall(parcel, "readHashMap", 1, *cl)));
-}
-
-void
-MapType::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl)
-{
- EnsureClassLoader(addTo, cl);
- addTo->Add(new MethodCall(parcel, "readMap", 2, v, *cl));
-}
-
-
-// ================================================================
-
-ListType::ListType()
- :Type("java.util", "List", BUILT_IN, true, true)
-{
-}
-
-string
-ListType::InstantiableName() const
-{
- return "java.util.ArrayList";
-}
-
-void
-ListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeList", 1, v));
-}
-
-void
-ListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl)
-{
- EnsureClassLoader(addTo, cl);
- addTo->Add(new Assignment(v, new MethodCall(parcel, "readArrayList", 1, *cl)));
-}
-
-void
-ListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl)
-{
- EnsureClassLoader(addTo, cl);
- addTo->Add(new MethodCall(parcel, "readList", 2, v, *cl));
-}
-
-// ================================================================
-
-UserDataType::UserDataType(const string& package, const string& name,
- bool builtIn, bool canWriteToParcel,
- const string& declFile, int declLine)
- :Type(package, name, builtIn ? BUILT_IN : USERDATA, canWriteToParcel,
- true, declFile, declLine)
-{
-}
-
-string
-UserDataType::CreatorName() const
-{
- return QualifiedName() + ".CREATOR";
-}
-
-void
-UserDataType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- // if (v != null) {
- // parcel.writeInt(1);
- // v.writeToParcel(parcel);
- // } else {
- // parcel.writeInt(0);
- // }
- IfStatement* elsepart = new IfStatement();
- elsepart->statements->Add(new MethodCall(parcel, "writeInt", 1,
- new LiteralExpression("0")));
- IfStatement* ifpart = new IfStatement;
- ifpart->expression = new Comparison(v, "!=", NULL_VALUE);
- ifpart->elseif = elsepart;
- ifpart->statements->Add(new MethodCall(parcel, "writeInt", 1,
- new LiteralExpression("1")));
- ifpart->statements->Add(new MethodCall(v, "writeToParcel", 2,
- parcel, BuildWriteToParcelFlags(flags)));
-
- addTo->Add(ifpart);
-}
-
-void
-UserDataType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- // if (0 != parcel.readInt()) {
- // v = CLASS.CREATOR.createFromParcel(parcel)
- // } else {
- // v = null;
- // }
- IfStatement* elsepart = new IfStatement();
- elsepart->statements->Add(new Assignment(v, NULL_VALUE));
-
- IfStatement* ifpart = new IfStatement();
- ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
- new MethodCall(parcel, "readInt"));
- ifpart->elseif = elsepart;
- ifpart->statements->Add(new Assignment(v,
- new MethodCall(v->type, "CREATOR.createFromParcel", 1, parcel)));
-
- addTo->Add(ifpart);
-}
-
-void
-UserDataType::ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- // TODO: really, we don't need to have this extra check, but we
- // don't have two separate marshalling code paths
- // if (0 != parcel.readInt()) {
- // v.readFromParcel(parcel)
- // }
- IfStatement* ifpart = new IfStatement();
- ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
- new MethodCall(parcel, "readInt"));
- ifpart->statements->Add(new MethodCall(v, "readFromParcel", 1, parcel));
- addTo->Add(ifpart);
-}
-
-bool
-UserDataType::CanBeArray() const
-{
- return true;
-}
-
-void
-UserDataType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- addTo->Add(new MethodCall(parcel, "writeTypedArray", 2, v,
- BuildWriteToParcelFlags(flags)));
-}
-
-void
-UserDataType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- string creator = v->type->QualifiedName() + ".CREATOR";
- addTo->Add(new Assignment(v, new MethodCall(parcel,
- "createTypedArray", 1, new LiteralExpression(creator))));
-}
-
-void
-UserDataType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- string creator = v->type->QualifiedName() + ".CREATOR";
- addTo->Add(new MethodCall(parcel, "readTypedArray", 2,
- v, new LiteralExpression(creator)));
-}
-
-// ================================================================
-
-InterfaceType::InterfaceType(const string& package, const string& name,
- bool builtIn, bool oneway,
- const string& declFile, int declLine)
- :Type(package, name, builtIn ? BUILT_IN : INTERFACE, true, false,
- declFile, declLine)
- ,m_oneway(oneway)
-{
-}
-
-bool
-InterfaceType::OneWay() const
-{
- return m_oneway;
-}
-
-void
-InterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- // parcel.writeStrongBinder(v != null ? v.asBinder() : null);
- addTo->Add(new MethodCall(parcel, "writeStrongBinder", 1,
- new Ternary(
- new Comparison(v, "!=", NULL_VALUE),
- new MethodCall(v, "asBinder"),
- NULL_VALUE)));
-}
-
-void
-InterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- // v = Interface.asInterface(parcel.readStrongBinder());
- string type = v->type->QualifiedName();
- type += ".Stub";
- addTo->Add(new Assignment(v,
- new MethodCall( NAMES.Find(type), "asInterface", 1,
- new MethodCall(parcel, "readStrongBinder"))));
-}
-
-
-// ================================================================
-
-GenericType::GenericType(const string& package, const string& name,
- const vector<Type*>& args)
- :Type(package, name, BUILT_IN, true, true)
-{
- m_args = args;
-
- m_importName = package + '.' + name;
-
- string gen = "<";
- int N = args.size();
- for (int i=0; i<N; i++) {
- Type* t = args[i];
- gen += t->QualifiedName();
- if (i != N-1) {
- gen += ',';
- }
- }
- gen += '>';
- m_genericArguments = gen;
- SetQualifiedName(m_importName + gen);
-}
-
-const vector<Type*>&
-GenericType::GenericArgumentTypes() const
-{
- return m_args;
-}
-
-string
-GenericType::GenericArguments() const
-{
- return m_genericArguments;
-}
-
-string
-GenericType::ImportType() const
-{
- return m_importName;
-}
-
-void
-GenericType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- fprintf(stderr, "implement GenericType::WriteToParcel\n");
-}
-
-void
-GenericType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- fprintf(stderr, "implement GenericType::CreateFromParcel\n");
-}
-
-void
-GenericType::ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- fprintf(stderr, "implement GenericType::ReadFromParcel\n");
-}
-
-
-// ================================================================
-
-GenericListType::GenericListType(const string& package, const string& name,
- const vector<Type*>& args)
- :GenericType(package, name, args),
- m_creator(args[0]->CreatorName())
-{
-}
-
-string
-GenericListType::CreatorName() const
-{
- return "android.os.Parcel.arrayListCreator";
-}
-
-string
-GenericListType::InstantiableName() const
-{
- return "java.util.ArrayList" + GenericArguments();
-}
-
-void
-GenericListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
-{
- if (m_creator == STRING_TYPE->CreatorName()) {
- addTo->Add(new MethodCall(parcel, "writeStringList", 1, v));
- } else if (m_creator == IBINDER_TYPE->CreatorName()) {
- addTo->Add(new MethodCall(parcel, "writeBinderList", 1, v));
- } else {
- // parcel.writeTypedListXX(arg);
- addTo->Add(new MethodCall(parcel, "writeTypedList", 1, v));
- }
-}
-
-void
-GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
-{
- if (m_creator == STRING_TYPE->CreatorName()) {
- addTo->Add(new Assignment(v,
- new MethodCall(parcel, "createStringArrayList", 0)));
- } else if (m_creator == IBINDER_TYPE->CreatorName()) {
- addTo->Add(new Assignment(v,
- new MethodCall(parcel, "createBinderArrayList", 0)));
- } else {
- // v = _data.readTypedArrayList(XXX.creator);
- addTo->Add(new Assignment(v,
- new MethodCall(parcel, "createTypedArrayList", 1,
- new LiteralExpression(m_creator))));
- }
-}
-
-void
-GenericListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable**)
-{
- if (m_creator == STRING_TYPE->CreatorName()) {
- addTo->Add(new MethodCall(parcel, "readStringList", 1, v));
- } else if (m_creator == IBINDER_TYPE->CreatorName()) {
- addTo->Add(new MethodCall(parcel, "readBinderList", 1, v));
- } else {
- // v = _data.readTypedList(v, XXX.creator);
- addTo->Add(new MethodCall(parcel, "readTypedList", 2,
- v,
- new LiteralExpression(m_creator)));
- }
-}
-
-
-// ================================================================
-
-ClassLoaderType::ClassLoaderType()
- :Type("java.lang", "ClassLoader", BUILT_IN, false, false)
-{
-}
-
-
-// ================================================================
-
-Namespace::Namespace()
-{
-}
-
-Namespace::~Namespace()
-{
- int N = m_types.size();
- for (int i=0; i<N; i++) {
- delete m_types[i];
- }
-}
-
-void
-Namespace::Add(Type* type)
-{
- Type* t = Find(type->QualifiedName());
- if (t == NULL) {
- m_types.push_back(type);
- }
-}
-
-void
-Namespace::AddGenericType(const string& package, const string& name, int args)
-{
- Generic g;
- g.package = package;
- g.name = name;
- g.qualified = package + '.' + name;
- g.args = args;
- m_generics.push_back(g);
-}
-
-Type*
-Namespace::Find(const string& name) const
-{
- int N = m_types.size();
- for (int i=0; i<N; i++) {
- if (m_types[i]->QualifiedName() == name) {
- return m_types[i];
- }
- }
- return NULL;
-}
-
-Type*
-Namespace::Find(const char* package, const char* name) const
-{
- string s;
- if (package != NULL) {
- s += package;
- s += '.';
- }
- s += name;
- return Find(s);
-}
-
-static string
-normalize_generic(const string& s)
-{
- string r;
- int N = s.size();
- for (int i=0; i<N; i++) {
- char c = s[i];
- if (!isspace(c)) {
- r += c;
- }
- }
- return r;
-}
-
-Type*
-Namespace::Search(const string& name)
-{
- // an exact match wins
- Type* result = Find(name);
- if (result != NULL) {
- return result;
- }
-
- // try the class names
- // our language doesn't allow you to not specify outer classes
- // when referencing an inner class. that could be changed, and this
- // would be the place to do it, but I don't think the complexity in
- // scoping rules is worth it.
- int N = m_types.size();
- for (int i=0; i<N; i++) {
- if (m_types[i]->Name() == name) {
- return m_types[i];
- }
- }
-
- // we got to here and it's not a generic, give up
- if (name.find('<') == name.npos) {
- return NULL;
- }
-
- // remove any whitespace
- string normalized = normalize_generic(name);
-
- // find the part before the '<', find a generic for it
- ssize_t baseIndex = normalized.find('<');
- string base(normalized.c_str(), baseIndex);
- const Generic* g = search_generic(base);
- if (g == NULL) {
- return NULL;
- }
-
- // For each of the args, do a recursive search on it. We don't allow
- // generics within generics like Java does, because we're really limiting
- // them to just built-in container classes, at least for now. Our syntax
- // ensures this right now as well.
- vector<Type*> args;
- size_t start = baseIndex + 1;
- size_t end = start;
- while (normalized[start] != '\0') {
- end = normalized.find(',', start);
- if (end == normalized.npos) {
- end = normalized.find('>', start);
- }
- string s(normalized.c_str()+start, end-start);
- Type* t = this->Search(s);
- if (t == NULL) {
- // maybe we should print a warning here?
- return NULL;
- }
- args.push_back(t);
- start = end+1;
- }
-
- // construct a GenericType, add it to our name set so they always get
- // the same object, and return it.
- result = make_generic_type(g->package, g->name, args);
- if (result == NULL) {
- return NULL;
- }
-
- this->Add(result);
- return this->Find(result->QualifiedName());
-}
-
-const Namespace::Generic*
-Namespace::search_generic(const string& name) const
-{
- int N = m_generics.size();
-
- // first exact match
- for (int i=0; i<N; i++) {
- const Generic& g = m_generics[i];
- if (g.qualified == name) {
- return &g;
- }
- }
-
- // then name match
- for (int i=0; i<N; i++) {
- const Generic& g = m_generics[i];
- if (g.name == name) {
- return &g;
- }
- }
-
- return NULL;
-}
-
-void
-Namespace::Dump() const
-{
- int n = m_types.size();
- for (int i=0; i<n; i++) {
- Type* t = m_types[i];
- printf("type: package=%s name=%s qualifiedName=%s\n",
- t->Package().c_str(), t->Name().c_str(),
- t->QualifiedName().c_str());
- }
-}
diff --git a/tools/aidl/Type.h b/tools/aidl/Type.h
deleted file mode 100644
index 6ede79a..0000000
--- a/tools/aidl/Type.h
+++ /dev/null
@@ -1,472 +0,0 @@
-#ifndef AIDL_TYPE_H_
-#define AIDL_TYPE_H_
-
-#include "AST.h"
-#include <string>
-#include <vector>
-
-using namespace std;
-
-class Type
-{
-public:
- // kinds
- enum {
- BUILT_IN,
- USERDATA,
- INTERFACE,
- GENERATED
- };
-
- // WriteToParcel flags
- enum {
- PARCELABLE_WRITE_RETURN_VALUE = 0x0001
- };
-
- Type(const string& name, int kind, bool canWriteToParcel,
- bool canBeOut);
- Type(const string& package, const string& name,
- int kind, bool canWriteToParcel, bool canBeOut,
- const string& declFile = "", int declLine = -1);
- virtual ~Type();
-
- inline string Package() const { return m_package; }
- inline string Name() const { return m_name; }
- inline string QualifiedName() const { return m_qualifiedName; }
- inline int Kind() const { return m_kind; }
- inline string DeclFile() const { return m_declFile; }
- inline int DeclLine() const { return m_declLine; }
- inline bool CanWriteToParcel() const { return m_canWriteToParcel; }
- inline bool CanBeOutParameter() const { return m_canBeOut; }
-
- virtual string ImportType() const;
- virtual string CreatorName() const;
- virtual string InstantiableName() const;
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
- virtual bool CanBeArray() const;
-
- virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
-protected:
- void SetQualifiedName(const string& qualified);
- Expression* BuildWriteToParcelFlags(int flags);
-
-private:
- Type();
- Type(const Type&);
-
- string m_package;
- string m_name;
- string m_qualifiedName;
- string m_declFile;
- int m_declLine;
- int m_kind;
- bool m_canWriteToParcel;
- bool m_canBeOut;
-};
-
-class BasicType : public Type
-{
-public:
- BasicType(const string& name,
- const string& marshallParcel,
- const string& unmarshallParcel,
- const string& writeArrayParcel,
- const string& createArrayParcel,
- const string& readArrayParcel);
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
- virtual bool CanBeArray() const;
-
- virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
-private:
- string m_marshallParcel;
- string m_unmarshallParcel;
- string m_writeArrayParcel;
- string m_createArrayParcel;
- string m_readArrayParcel;
-};
-
-class BooleanType : public Type
-{
-public:
- BooleanType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
- virtual bool CanBeArray() const;
-
- virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class CharType : public Type
-{
-public:
- CharType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
- virtual bool CanBeArray() const;
-
- virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-
-class StringType : public Type
-{
-public:
- StringType();
-
- virtual string CreatorName() const;
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
- virtual bool CanBeArray() const;
-
- virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class CharSequenceType : public Type
-{
-public:
- CharSequenceType();
-
- virtual string CreatorName() const;
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class RemoteExceptionType : public Type
-{
-public:
- RemoteExceptionType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class RuntimeExceptionType : public Type
-{
-public:
- RuntimeExceptionType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class IBinderType : public Type
-{
-public:
- IBinderType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
- virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class IInterfaceType : public Type
-{
-public:
- IInterfaceType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class BinderType : public Type
-{
-public:
- BinderType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class BinderProxyType : public Type
-{
-public:
- BinderProxyType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class ParcelType : public Type
-{
-public:
- ParcelType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class ParcelableInterfaceType : public Type
-{
-public:
- ParcelableInterfaceType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class MapType : public Type
-{
-public:
- MapType();
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class ListType : public Type
-{
-public:
- ListType();
-
- virtual string InstantiableName() const;
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class UserDataType : public Type
-{
-public:
- UserDataType(const string& package, const string& name,
- bool builtIn, bool canWriteToParcel,
- const string& declFile = "", int declLine = -1);
-
- virtual string CreatorName() const;
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
- virtual bool CanBeArray() const;
-
- virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-};
-
-class InterfaceType : public Type
-{
-public:
- InterfaceType(const string& package, const string& name,
- bool builtIn, bool oneway,
- const string& declFile, int declLine);
-
- bool OneWay() const;
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
-private:
- bool m_oneway;
-};
-
-
-class GenericType : public Type
-{
-public:
- GenericType(const string& package, const string& name,
- const vector<Type*>& args);
-
- const vector<Type*>& GenericArgumentTypes() const;
- string GenericArguments() const;
-
- virtual string ImportType() const;
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
-private:
- string m_genericArguments;
- string m_importName;
- vector<Type*> m_args;
-};
-
-class ClassLoaderType : public Type
-{
-public:
- ClassLoaderType();
-};
-
-class GenericListType : public GenericType
-{
-public:
- GenericListType(const string& package, const string& name,
- const vector<Type*>& args);
-
- virtual string CreatorName() const;
- virtual string InstantiableName() const;
-
- virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags);
- virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
- virtual void ReadFromParcel(StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl);
-
-private:
- string m_creator;
-};
-
-class Namespace
-{
-public:
- Namespace();
- ~Namespace();
- void Add(Type* type);
-
- // args is the number of template types (what is this called?)
- void AddGenericType(const string& package, const string& name, int args);
-
- // lookup a specific class name
- Type* Find(const string& name) const;
- Type* Find(const char* package, const char* name) const;
-
- // try to search by either a full name or a partial name
- Type* Search(const string& name);
-
- void Dump() const;
-
-private:
- struct Generic {
- string package;
- string name;
- string qualified;
- int args;
- };
-
- const Generic* search_generic(const string& name) const;
-
- vector<Type*> m_types;
- vector<Generic> m_generics;
-};
-
-extern Namespace NAMES;
-
-extern Type* VOID_TYPE;
-extern Type* BOOLEAN_TYPE;
-extern Type* BYTE_TYPE;
-extern Type* CHAR_TYPE;
-extern Type* INT_TYPE;
-extern Type* LONG_TYPE;
-extern Type* FLOAT_TYPE;
-extern Type* DOUBLE_TYPE;
-extern Type* OBJECT_TYPE;
-extern Type* STRING_TYPE;
-extern Type* CHAR_SEQUENCE_TYPE;
-extern Type* TEXT_UTILS_TYPE;
-extern Type* REMOTE_EXCEPTION_TYPE;
-extern Type* RUNTIME_EXCEPTION_TYPE;
-extern Type* IBINDER_TYPE;
-extern Type* IINTERFACE_TYPE;
-extern Type* BINDER_NATIVE_TYPE;
-extern Type* BINDER_PROXY_TYPE;
-extern Type* PARCEL_TYPE;
-extern Type* PARCELABLE_INTERFACE_TYPE;
-
-extern Type* CONTEXT_TYPE;
-
-extern Expression* NULL_VALUE;
-extern Expression* THIS_VALUE;
-extern Expression* SUPER_VALUE;
-extern Expression* TRUE_VALUE;
-extern Expression* FALSE_VALUE;
-
-void register_base_types();
-
-#endif // AIDL_TYPE_H_
diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp
deleted file mode 100644
index 832c51c..0000000
--- a/tools/aidl/aidl.cpp
+++ /dev/null
@@ -1,1070 +0,0 @@
-
-#include "aidl_language.h"
-#include "options.h"
-#include "os.h"
-#include "search_path.h"
-#include "Type.h"
-#include "generate_java.h"
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <map>
-
-#ifdef _WIN32
-#include <io.h>
-#include <direct.h>
-#include <sys/stat.h>
-#endif
-
-#ifndef O_BINARY
-# define O_BINARY 0
-#endif
-
-// The following are gotten as the offset from the allowable id's between
-// android.os.IBinder.FIRST_CALL_TRANSACTION=1 and
-// android.os.IBinder.LAST_CALL_TRANSACTION=16777215
-#define MIN_USER_SET_METHOD_ID 0
-#define MAX_USER_SET_METHOD_ID 16777214
-
-using namespace std;
-
-static void
-test_document(document_item_type* d)
-{
- while (d) {
- if (d->item_type == INTERFACE_TYPE_BINDER) {
- interface_type* c = (interface_type*)d;
- printf("interface %s %s {\n", c->package, c->name.data);
- interface_item_type *q = (interface_item_type*)c->interface_items;
- while (q) {
- if (q->item_type == METHOD_TYPE) {
- method_type *m = (method_type*)q;
- printf(" %s %s(", m->type.type.data, m->name.data);
- arg_type *p = m->args;
- while (p) {
- printf("%s %s",p->type.type.data,p->name.data);
- if (p->next) printf(", ");
- p=p->next;
- }
- printf(")");
- printf(";\n");
- }
- q=q->next;
- }
- printf("}\n");
- }
- else if (d->item_type == USER_DATA_TYPE) {
- user_data_type* b = (user_data_type*)d;
- if (b->parcelable) {
- printf("parcelable %s %s;\n", b->package, b->name.data);
- }
- }
- else {
- printf("UNKNOWN d=0x%08lx d->item_type=%d\n", (long)d, d->item_type);
- }
- d = d->next;
- }
-}
-
-// ==========================================================
-int
-convert_direction(const char* direction)
-{
- if (direction == NULL) {
- return IN_PARAMETER;
- }
- if (0 == strcmp(direction, "in")) {
- return IN_PARAMETER;
- }
- if (0 == strcmp(direction, "out")) {
- return OUT_PARAMETER;
- }
- return INOUT_PARAMETER;
-}
-
-// ==========================================================
-struct import_info {
- const char* from;
- const char* filename;
- buffer_type statement;
- const char* neededClass;
- document_item_type* doc;
- struct import_info* next;
-};
-
-document_item_type* g_document = NULL;
-import_info* g_imports = NULL;
-
-static void
-main_document_parsed(document_item_type* d)
-{
- g_document = d;
-}
-
-static void
-main_import_parsed(buffer_type* statement)
-{
- import_info* import = (import_info*)malloc(sizeof(import_info));
- memset(import, 0, sizeof(import_info));
- import->from = strdup(g_currentFilename);
- import->statement.lineno = statement->lineno;
- import->statement.data = strdup(statement->data);
- import->statement.extra = NULL;
- import->next = g_imports;
- import->neededClass = parse_import_statement(statement->data);
- g_imports = import;
-}
-
-static ParserCallbacks g_mainCallbacks = {
- &main_document_parsed,
- &main_import_parsed
-};
-
-char*
-parse_import_statement(const char* text)
-{
- const char* end;
- int len;
-
- while (isspace(*text)) {
- text++;
- }
- while (!isspace(*text)) {
- text++;
- }
- while (isspace(*text)) {
- text++;
- }
- end = text;
- while (!isspace(*end) && *end != ';') {
- end++;
- }
- len = end-text;
-
- char* rv = (char*)malloc(len+1);
- memcpy(rv, text, len);
- rv[len] = '\0';
-
- return rv;
-}
-
-// ==========================================================
-static void
-import_import_parsed(buffer_type* statement)
-{
-}
-
-// ==========================================================
-static int
-check_filename(const char* filename, const char* package, buffer_type* name)
-{
- const char* p;
- string expected;
- string fn;
- size_t len;
- char cwd[MAXPATHLEN];
- bool valid = false;
-
-#ifdef _WIN32
- if (isalpha(filename[0]) && filename[1] == ':'
- && filename[2] == OS_PATH_SEPARATOR) {
-#else
- if (filename[0] == OS_PATH_SEPARATOR) {
-#endif
- fn = filename;
- } else {
- fn = getcwd(cwd, sizeof(cwd));
- len = fn.length();
- if (fn[len-1] != OS_PATH_SEPARATOR) {
- fn += OS_PATH_SEPARATOR;
- }
- fn += filename;
- }
-
- if (package) {
- expected = package;
- expected += '.';
- }
-
- len = expected.length();
- for (size_t i=0; i<len; i++) {
- if (expected[i] == '.') {
- expected[i] = OS_PATH_SEPARATOR;
- }
- }
-
- p = strchr(name->data, '.');
- len = p ? p-name->data : strlen(name->data);
- expected.append(name->data, len);
-
- expected += ".aidl";
-
- len = fn.length();
- valid = (len >= expected.length());
-
- if (valid) {
- p = fn.c_str() + (len - expected.length());
-
-#ifdef _WIN32
- if (OS_PATH_SEPARATOR != '/') {
- // Input filename under cygwin most likely has / separators
- // whereas the expected string uses \\ separators. Adjust
- // them accordingly.
- for (char *c = const_cast<char *>(p); *c; ++c) {
- if (*c == '/') *c = OS_PATH_SEPARATOR;
- }
- }
-#endif
-
- // aidl assumes case-insensitivity on Mac Os and Windows.
-#if defined(__linux__)
- valid = (expected == p);
-#else
- valid = !strcasecmp(expected.c_str(), p);
-#endif
- }
-
- if (!valid) {
- fprintf(stderr, "%s:%d interface %s should be declared in a file"
- " called %s.\n",
- filename, name->lineno, name->data, expected.c_str());
- return 1;
- }
-
- return 0;
-}
-
-static int
-check_filenames(const char* filename, document_item_type* items)
-{
- int err = 0;
- while (items) {
- if (items->item_type == USER_DATA_TYPE) {
- user_data_type* p = (user_data_type*)items;
- err |= check_filename(filename, p->package, &p->name);
- }
- else if (items->item_type == INTERFACE_TYPE_BINDER) {
- interface_type* c = (interface_type*)items;
- err |= check_filename(filename, c->package, &c->name);
- }
- else {
- fprintf(stderr, "aidl: internal error unkown document type %d.\n",
- items->item_type);
- return 1;
- }
- items = items->next;
- }
- return err;
-}
-
-// ==========================================================
-static const char*
-kind_to_string(int kind)
-{
- switch (kind)
- {
- case Type::INTERFACE:
- return "an interface";
- case Type::USERDATA:
- return "a user data";
- default:
- return "ERROR";
- }
-}
-
-static char*
-rfind(char* str, char c)
-{
- char* p = str + strlen(str) - 1;
- while (p >= str) {
- if (*p == c) {
- return p;
- }
- p--;
- }
- return NULL;
-}
-
-static int
-gather_types(const char* filename, document_item_type* items)
-{
- int err = 0;
- while (items) {
- Type* type;
- if (items->item_type == USER_DATA_TYPE) {
- user_data_type* p = (user_data_type*)items;
- type = new UserDataType(p->package ? p->package : "", p->name.data,
- false, p->parcelable, filename, p->name.lineno);
- }
- else if (items->item_type == INTERFACE_TYPE_BINDER) {
- interface_type* c = (interface_type*)items;
- type = new InterfaceType(c->package ? c->package : "",
- c->name.data, false, c->oneway,
- filename, c->name.lineno);
- }
- else {
- fprintf(stderr, "aidl: internal error %s:%d\n", __FILE__, __LINE__);
- return 1;
- }
-
- Type* old = NAMES.Find(type->QualifiedName());
- if (old == NULL) {
- NAMES.Add(type);
-
- if (items->item_type == INTERFACE_TYPE_BINDER) {
- // for interfaces, also add the stub and proxy types, we don't
- // bother checking these for duplicates, because the parser
- // won't let us do it.
- interface_type* c = (interface_type*)items;
-
- string name = c->name.data;
- name += ".Stub";
- Type* stub = new Type(c->package ? c->package : "",
- name, Type::GENERATED, false, false,
- filename, c->name.lineno);
- NAMES.Add(stub);
-
- name = c->name.data;
- name += ".Stub.Proxy";
- Type* proxy = new Type(c->package ? c->package : "",
- name, Type::GENERATED, false, false,
- filename, c->name.lineno);
- NAMES.Add(proxy);
- }
- } else {
- if (old->Kind() == Type::BUILT_IN) {
- fprintf(stderr, "%s:%d attempt to redefine built in class %s\n",
- filename, type->DeclLine(),
- type->QualifiedName().c_str());
- err = 1;
- }
- else if (type->Kind() != old->Kind()) {
- const char* oldKind = kind_to_string(old->Kind());
- const char* newKind = kind_to_string(type->Kind());
-
- fprintf(stderr, "%s:%d attempt to redefine %s as %s,\n",
- filename, type->DeclLine(),
- type->QualifiedName().c_str(), newKind);
- fprintf(stderr, "%s:%d previously defined here as %s.\n",
- old->DeclFile().c_str(), old->DeclLine(), oldKind);
- err = 1;
- }
- }
-
- items = items->next;
- }
- return err;
-}
-
-// ==========================================================
-static bool
-matches_keyword(const char* str)
-{
- static const char* KEYWORDS[] = { "abstract", "assert", "boolean", "break",
- "byte", "case", "catch", "char", "class", "const", "continue",
- "default", "do", "double", "else", "enum", "extends", "final",
- "finally", "float", "for", "goto", "if", "implements", "import",
- "instanceof", "int", "interface", "long", "native", "new", "package",
- "private", "protected", "public", "return", "short", "static",
- "strictfp", "super", "switch", "synchronized", "this", "throw",
- "throws", "transient", "try", "void", "volatile", "while",
- "true", "false", "null",
- NULL
- };
- const char** k = KEYWORDS;
- while (*k) {
- if (0 == strcmp(str, *k)) {
- return true;
- }
- k++;
- }
- return false;
-}
-
-static int
-check_method(const char* filename, method_type* m)
-{
- int err = 0;
-
- // return type
- Type* returnType = NAMES.Search(m->type.type.data);
- if (returnType == NULL) {
- fprintf(stderr, "%s:%d unknown return type %s\n", filename,
- m->type.type.lineno, m->type.type.data);
- err = 1;
- return err;
- }
-
- if (!returnType->CanWriteToParcel()) {
- fprintf(stderr, "%s:%d return type %s can't be marshalled.\n", filename,
- m->type.type.lineno, m->type.type.data);
- err = 1;
- }
-
- if (m->type.dimension > 0 && !returnType->CanBeArray()) {
- fprintf(stderr, "%s:%d return type %s%s can't be an array.\n", filename,
- m->type.array_token.lineno, m->type.type.data,
- m->type.array_token.data);
- err = 1;
- }
-
- if (m->type.dimension > 1) {
- fprintf(stderr, "%s:%d return type %s%s only one"
- " dimensional arrays are supported\n", filename,
- m->type.array_token.lineno, m->type.type.data,
- m->type.array_token.data);
- err = 1;
- }
-
- int index = 1;
-
- arg_type* arg = m->args;
- while (arg) {
- Type* t = NAMES.Search(arg->type.type.data);
-
- // check the arg type
- if (t == NULL) {
- fprintf(stderr, "%s:%d parameter %s (%d) unknown type %s\n",
- filename, m->type.type.lineno, arg->name.data, index,
- arg->type.type.data);
- err = 1;
- goto next;
- }
-
- if (!t->CanWriteToParcel()) {
- fprintf(stderr, "%s:%d parameter %d: '%s %s' can't be marshalled.\n",
- filename, m->type.type.lineno, index,
- arg->type.type.data, arg->name.data);
- err = 1;
- }
-
- if (arg->direction.data == NULL
- && (arg->type.dimension != 0 || t->CanBeOutParameter())) {
- fprintf(stderr, "%s:%d parameter %d: '%s %s' can be an out"
- " parameter, so you must declare it as in,"
- " out or inout.\n",
- filename, m->type.type.lineno, index,
- arg->type.type.data, arg->name.data);
- err = 1;
- }
-
- if (convert_direction(arg->direction.data) != IN_PARAMETER
- && !t->CanBeOutParameter()
- && arg->type.dimension == 0) {
- fprintf(stderr, "%s:%d parameter %d: '%s %s %s' can only be an in"
- " parameter.\n",
- filename, m->type.type.lineno, index,
- arg->direction.data, arg->type.type.data,
- arg->name.data);
- err = 1;
- }
-
- if (arg->type.dimension > 0 && !t->CanBeArray()) {
- fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' can't be an"
- " array.\n", filename,
- m->type.array_token.lineno, index, arg->direction.data,
- arg->type.type.data, arg->type.array_token.data,
- arg->name.data);
- err = 1;
- }
-
- if (arg->type.dimension > 1) {
- fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' only one"
- " dimensional arrays are supported\n", filename,
- m->type.array_token.lineno, index, arg->direction.data,
- arg->type.type.data, arg->type.array_token.data,
- arg->name.data);
- err = 1;
- }
-
- // check that the name doesn't match a keyword
- if (matches_keyword(arg->name.data)) {
- fprintf(stderr, "%s:%d parameter %d %s is named the same as a"
- " Java or aidl keyword\n",
- filename, m->name.lineno, index, arg->name.data);
- err = 1;
- }
-
-next:
- index++;
- arg = arg->next;
- }
-
- return err;
-}
-
-static int
-check_types(const char* filename, document_item_type* items)
-{
- int err = 0;
- while (items) {
- // (nothing to check for USER_DATA_TYPE)
- if (items->item_type == INTERFACE_TYPE_BINDER) {
- map<string,method_type*> methodNames;
- interface_type* c = (interface_type*)items;
-
- interface_item_type* member = c->interface_items;
- while (member) {
- if (member->item_type == METHOD_TYPE) {
- method_type* m = (method_type*)member;
-
- err |= check_method(filename, m);
-
- // prevent duplicate methods
- if (methodNames.find(m->name.data) == methodNames.end()) {
- methodNames[m->name.data] = m;
- } else {
- fprintf(stderr,"%s:%d attempt to redefine method %s,\n",
- filename, m->name.lineno, m->name.data);
- method_type* old = methodNames[m->name.data];
- fprintf(stderr, "%s:%d previously defined here.\n",
- filename, old->name.lineno);
- err = 1;
- }
- }
- member = member->next;
- }
- }
-
- items = items->next;
- }
- return err;
-}
-
-// ==========================================================
-static int
-exactly_one_interface(const char* filename, const document_item_type* items, const Options& options,
- bool* onlyParcelable)
-{
- if (items == NULL) {
- fprintf(stderr, "%s: file does not contain any interfaces\n",
- filename);
- return 1;
- }
-
- const document_item_type* next = items->next;
- // Allow parcelables to skip the "one-only" rule.
- if (items->next != NULL && next->item_type != USER_DATA_TYPE) {
- int lineno = -1;
- if (next->item_type == INTERFACE_TYPE_BINDER) {
- lineno = ((interface_type*)next)->interface_token.lineno;
- }
- fprintf(stderr, "%s:%d aidl can only handle one interface per file\n",
- filename, lineno);
- return 1;
- }
-
- if (items->item_type == USER_DATA_TYPE) {
- *onlyParcelable = true;
- if (options.failOnParcelable) {
- fprintf(stderr, "%s:%d aidl can only generate code for interfaces, not"
- " parcelables,\n", filename,
- ((user_data_type*)items)->keyword_token.lineno);
- fprintf(stderr, "%s:%d .aidl files that only declare parcelables"
- "may not go in the Makefile.\n", filename,
- ((user_data_type*)items)->keyword_token.lineno);
- return 1;
- }
- } else {
- *onlyParcelable = false;
- }
-
- return 0;
-}
-
-// ==========================================================
-void
-generate_dep_file(const Options& options, const document_item_type* items)
-{
- /* we open the file in binary mode to ensure that the same output is
- * generated on all platforms !!
- */
- FILE* to = NULL;
- if (options.autoDepFile) {
- string fileName = options.outputFileName + ".d";
- to = fopen(fileName.c_str(), "wb");
- } else {
- to = fopen(options.depFileName.c_str(), "wb");
- }
-
- if (to == NULL) {
- return;
- }
-
- const char* slash = "\\";
- import_info* import = g_imports;
- if (import == NULL) {
- slash = "";
- }
-
- if (items->item_type == INTERFACE_TYPE_BINDER) {
- fprintf(to, "%s: \\\n", options.outputFileName.c_str());
- } else {
- // parcelable: there's no output file.
- fprintf(to, " : \\\n");
- }
- fprintf(to, " %s %s\n", options.inputFileName.c_str(), slash);
-
- while (import) {
- if (import->next == NULL) {
- slash = "";
- }
- if (import->filename) {
- fprintf(to, " %s %s\n", import->filename, slash);
- }
- import = import->next;
- }
-
- fprintf(to, "\n");
-
- // Output "<input_aidl_file>: " so make won't fail if the input .aidl file
- // has been deleted, moved or renamed in incremental build.
- fprintf(to, "%s :\n", options.inputFileName.c_str());
-
- // Output "<imported_file>: " so make won't fail if the imported file has
- // been deleted, moved or renamed in incremental build.
- import = g_imports;
- while (import) {
- if (import->filename) {
- fprintf(to, "%s :\n", import->filename);
- }
- import = import->next;
- }
-
- fclose(to);
-}
-
-// ==========================================================
-static string
-generate_outputFileName2(const Options& options, const buffer_type& name, const char* package)
-{
- string result;
-
- // create the path to the destination folder based on the
- // interface package name
- result = options.outputBaseFolder;
- result += OS_PATH_SEPARATOR;
-
- string packageStr = package;
- size_t len = packageStr.length();
- for (size_t i=0; i<len; i++) {
- if (packageStr[i] == '.') {
- packageStr[i] = OS_PATH_SEPARATOR;
- }
- }
-
- result += packageStr;
-
- // add the filename by replacing the .aidl extension to .java
- const char* p = strchr(name.data, '.');
- len = p ? p-name.data : strlen(name.data);
-
- result += OS_PATH_SEPARATOR;
- result.append(name.data, len);
- result += ".java";
-
- return result;
-}
-
-// ==========================================================
-static string
-generate_outputFileName(const Options& options, const document_item_type* items)
-{
- // items has already been checked to have only one interface.
- if (items->item_type == INTERFACE_TYPE_BINDER) {
- interface_type* type = (interface_type*)items;
-
- return generate_outputFileName2(options, type->name, type->package);
- } else if (items->item_type == USER_DATA_TYPE) {
- user_data_type* type = (user_data_type*)items;
- return generate_outputFileName2(options, type->name, type->package);
- }
-
- // I don't think we can come here, but safer than returning NULL.
- string result;
- return result;
-}
-
-
-
-// ==========================================================
-static void
-check_outputFilePath(const string& path) {
- size_t len = path.length();
- for (size_t i=0; i<len ; i++) {
- if (path[i] == OS_PATH_SEPARATOR) {
- string p = path.substr(0, i);
- if (access(path.data(), F_OK) != 0) {
-#ifdef _WIN32
- _mkdir(p.data());
-#else
- mkdir(p.data(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
-#endif
- }
- }
- }
-}
-
-
-// ==========================================================
-static int
-parse_preprocessed_file(const string& filename)
-{
- int err;
-
- FILE* f = fopen(filename.c_str(), "rb");
- if (f == NULL) {
- fprintf(stderr, "aidl: can't open preprocessed file: %s\n",
- filename.c_str());
- return 1;
- }
-
- int lineno = 1;
- char line[1024];
- char type[1024];
- char fullname[1024];
- while (fgets(line, sizeof(line), f)) {
- // skip comments and empty lines
- if (!line[0] || strncmp(line, "//", 2) == 0) {
- continue;
- }
-
- sscanf(line, "%s %[^; \r\n\t];", type, fullname);
-
- char* packagename;
- char* classname = rfind(fullname, '.');
- if (classname != NULL) {
- *classname = '\0';
- classname++;
- packagename = fullname;
- } else {
- classname = fullname;
- packagename = NULL;
- }
-
- //printf("%s:%d:...%s...%s...%s...\n", filename.c_str(), lineno,
- // type, packagename, classname);
- document_item_type* doc;
-
- if (0 == strcmp("parcelable", type)) {
- user_data_type* parcl = (user_data_type*)malloc(
- sizeof(user_data_type));
- memset(parcl, 0, sizeof(user_data_type));
- parcl->document_item.item_type = USER_DATA_TYPE;
- parcl->keyword_token.lineno = lineno;
- parcl->keyword_token.data = strdup(type);
- parcl->package = packagename ? strdup(packagename) : NULL;
- parcl->name.lineno = lineno;
- parcl->name.data = strdup(classname);
- parcl->semicolon_token.lineno = lineno;
- parcl->semicolon_token.data = strdup(";");
- parcl->parcelable = true;
- doc = (document_item_type*)parcl;
- }
- else if (0 == strcmp("interface", type)) {
- interface_type* iface = (interface_type*)malloc(
- sizeof(interface_type));
- memset(iface, 0, sizeof(interface_type));
- iface->document_item.item_type = INTERFACE_TYPE_BINDER;
- iface->interface_token.lineno = lineno;
- iface->interface_token.data = strdup(type);
- iface->package = packagename ? strdup(packagename) : NULL;
- iface->name.lineno = lineno;
- iface->name.data = strdup(classname);
- iface->open_brace_token.lineno = lineno;
- iface->open_brace_token.data = strdup("{");
- iface->close_brace_token.lineno = lineno;
- iface->close_brace_token.data = strdup("}");
- doc = (document_item_type*)iface;
- }
- else {
- fprintf(stderr, "%s:%d: bad type in line: %s\n",
- filename.c_str(), lineno, line);
- fclose(f);
- return 1;
- }
- err = gather_types(filename.c_str(), doc);
- lineno++;
- }
-
- if (!feof(f)) {
- fprintf(stderr, "%s:%d: error reading file, line to long.\n",
- filename.c_str(), lineno);
- return 1;
- }
-
- fclose(f);
- return 0;
-}
-
-static int
-check_and_assign_method_ids(const char * filename, interface_item_type* first_item)
-{
- // Check whether there are any methods with manually assigned id's and any that are not.
- // Either all method id's must be manually assigned or all of them must not.
- // Also, check for duplicates of user set id's and that the id's are within the proper bounds.
- set<int> usedIds;
- interface_item_type* item = first_item;
- bool hasUnassignedIds = false;
- bool hasAssignedIds = false;
- while (item != NULL) {
- if (item->item_type == METHOD_TYPE) {
- method_type* method_item = (method_type*)item;
- if (method_item->hasId) {
- hasAssignedIds = true;
- method_item->assigned_id = atoi(method_item->id.data);
- // Ensure that the user set id is not duplicated.
- if (usedIds.find(method_item->assigned_id) != usedIds.end()) {
- // We found a duplicate id, so throw an error.
- fprintf(stderr,
- "%s:%d Found duplicate method id (%d) for method: %s\n",
- filename, method_item->id.lineno,
- method_item->assigned_id, method_item->name.data);
- return 1;
- }
- // Ensure that the user set id is within the appropriate limits
- if (method_item->assigned_id < MIN_USER_SET_METHOD_ID ||
- method_item->assigned_id > MAX_USER_SET_METHOD_ID) {
- fprintf(stderr, "%s:%d Found out of bounds id (%d) for method: %s\n",
- filename, method_item->id.lineno,
- method_item->assigned_id, method_item->name.data);
- fprintf(stderr, " Value for id must be between %d and %d inclusive.\n",
- MIN_USER_SET_METHOD_ID, MAX_USER_SET_METHOD_ID);
- return 1;
- }
- usedIds.insert(method_item->assigned_id);
- } else {
- hasUnassignedIds = true;
- }
- if (hasAssignedIds && hasUnassignedIds) {
- fprintf(stderr,
- "%s: You must either assign id's to all methods or to none of them.\n",
- filename);
- return 1;
- }
- }
- item = item->next;
- }
-
- // In the case that all methods have unassigned id's, set a unique id for them.
- if (hasUnassignedIds) {
- int newId = 0;
- item = first_item;
- while (item != NULL) {
- if (item->item_type == METHOD_TYPE) {
- method_type* method_item = (method_type*)item;
- method_item->assigned_id = newId++;
- }
- item = item->next;
- }
- }
-
- // success
- return 0;
-}
-
-// ==========================================================
-int
-compile_aidl(Options& options)
-{
- int err = 0, N;
-
- set_import_paths(options.importPaths);
-
- register_base_types();
-
- // import the preprocessed file
- N = options.preprocessedFiles.size();
- for (int i=0; i<N; i++) {
- const string& s = options.preprocessedFiles[i];
- err |= parse_preprocessed_file(s);
- }
- if (err != 0) {
- return err;
- }
-
- // parse the main file
- g_callbacks = &g_mainCallbacks;
- err = parse_aidl(options.inputFileName.c_str());
- document_item_type* mainDoc = g_document;
- g_document = NULL;
-
- // parse the imports
- g_callbacks = &g_mainCallbacks;
- import_info* import = g_imports;
- while (import) {
- if (NAMES.Find(import->neededClass) == NULL) {
- import->filename = find_import_file(import->neededClass);
- if (!import->filename) {
- fprintf(stderr, "%s:%d: couldn't find import for class %s\n",
- import->from, import->statement.lineno,
- import->neededClass);
- err |= 1;
- } else {
- err |= parse_aidl(import->filename);
- import->doc = g_document;
- if (import->doc == NULL) {
- err |= 1;
- }
- }
- }
- import = import->next;
- }
- // bail out now if parsing wasn't successful
- if (err != 0 || mainDoc == NULL) {
- //fprintf(stderr, "aidl: parsing failed, stopping.\n");
- return 1;
- }
-
- // complain about ones that aren't in the right files
- err |= check_filenames(options.inputFileName.c_str(), mainDoc);
- import = g_imports;
- while (import) {
- err |= check_filenames(import->filename, import->doc);
- import = import->next;
- }
-
- // gather the types that have been declared
- err |= gather_types(options.inputFileName.c_str(), mainDoc);
- import = g_imports;
- while (import) {
- err |= gather_types(import->filename, import->doc);
- import = import->next;
- }
-
-#if 0
- printf("---- main doc ----\n");
- test_document(mainDoc);
-
- import = g_imports;
- while (import) {
- printf("---- import doc ----\n");
- test_document(import->doc);
- import = import->next;
- }
- NAMES.Dump();
-#endif
-
- // check the referenced types in mainDoc to make sure we've imported them
- err |= check_types(options.inputFileName.c_str(), mainDoc);
-
- // finally, there really only needs to be one thing in mainDoc, and it
- // needs to be an interface.
- bool onlyParcelable = false;
- err |= exactly_one_interface(options.inputFileName.c_str(), mainDoc, options, &onlyParcelable);
-
- // If this includes an interface definition, then assign method ids and validate.
- if (!onlyParcelable) {
- err |= check_and_assign_method_ids(options.inputFileName.c_str(),
- ((interface_type*)mainDoc)->interface_items);
- }
-
- // after this, there shouldn't be any more errors because of the
- // input.
- if (err != 0 || mainDoc == NULL) {
- return 1;
- }
-
- // if needed, generate the outputFileName from the outputBaseFolder
- if (options.outputFileName.length() == 0 &&
- options.outputBaseFolder.length() > 0) {
- options.outputFileName = generate_outputFileName(options, mainDoc);
- }
-
- // if we were asked to, generate a make dependency file
- // unless it's a parcelable *and* it's supposed to fail on parcelable
- if ((options.autoDepFile || options.depFileName != "") &&
- !(onlyParcelable && options.failOnParcelable)) {
- // make sure the folders of the output file all exists
- check_outputFilePath(options.outputFileName);
- generate_dep_file(options, mainDoc);
- }
-
- // they didn't ask to fail on parcelables, so just exit quietly.
- if (onlyParcelable && !options.failOnParcelable) {
- return 0;
- }
-
- // make sure the folders of the output file all exists
- check_outputFilePath(options.outputFileName);
-
- err = generate_java(options.outputFileName, options.inputFileName.c_str(),
- (interface_type*)mainDoc);
-
- return err;
-}
-
-int
-preprocess_aidl(const Options& options)
-{
- vector<string> lines;
- int err;
-
- // read files
- int N = options.filesToPreprocess.size();
- for (int i=0; i<N; i++) {
- g_callbacks = &g_mainCallbacks;
- err = parse_aidl(options.filesToPreprocess[i].c_str());
- if (err != 0) {
- return err;
- }
- document_item_type* doc = g_document;
- string line;
- if (doc->item_type == USER_DATA_TYPE) {
- user_data_type* parcelable = (user_data_type*)doc;
- if (parcelable->parcelable) {
- line = "parcelable ";
- }
- if (parcelable->package) {
- line += parcelable->package;
- line += '.';
- }
- line += parcelable->name.data;
- } else {
- line = "interface ";
- interface_type* iface = (interface_type*)doc;
- if (iface->package) {
- line += iface->package;
- line += '.';
- }
- line += iface->name.data;
- }
- line += ";\n";
- lines.push_back(line);
- }
-
- // write preprocessed file
- int fd = open( options.outputFileName.c_str(),
- O_RDWR|O_CREAT|O_TRUNC|O_BINARY,
-#ifdef _WIN32
- _S_IREAD|_S_IWRITE);
-#else
- S_IRUSR|S_IWUSR|S_IRGRP);
-#endif
- if (fd == -1) {
- fprintf(stderr, "aidl: could not open file for write: %s\n",
- options.outputFileName.c_str());
- return 1;
- }
-
- N = lines.size();
- for (int i=0; i<N; i++) {
- const string& s = lines[i];
- int len = s.length();
- if (len != write(fd, s.c_str(), len)) {
- fprintf(stderr, "aidl: error writing to file %s\n",
- options.outputFileName.c_str());
- close(fd);
- unlink(options.outputFileName.c_str());
- return 1;
- }
- }
-
- close(fd);
- return 0;
-}
diff --git a/tools/aidl/aidl.h b/tools/aidl/aidl.h
deleted file mode 100644
index 98b15f3..0000000
--- a/tools/aidl/aidl.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef AIDL_AIDL_H_
-#define AIDL_AIDL_H_
-
-#include "options.h"
-
-int compile_aidl(Options& options);
-int preprocess_aidl(const Options& options);
-
-#endif // AIDL_AIDL_H_
diff --git a/tools/aidl/aidl_language.cpp b/tools/aidl/aidl_language.cpp
deleted file mode 100644
index 5fab6c2..0000000
--- a/tools/aidl/aidl_language.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "aidl_language.h"
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#ifdef _WIN32
-int isatty(int fd)
-{
- return (fd == 0);
-}
-#endif
-
-#if 0
-ParserCallbacks k_parserCallbacks = {
- NULL
-};
-#endif
-
-ParserCallbacks* g_callbacks = NULL; // &k_parserCallbacks;
-
diff --git a/tools/aidl/aidl_language.h b/tools/aidl/aidl_language.h
deleted file mode 100644
index a3b1efc..0000000
--- a/tools/aidl/aidl_language.h
+++ /dev/null
@@ -1,165 +0,0 @@
-#ifndef AIDL_AIDL_LANGUAGE_H_
-#define AIDL_AIDL_LANGUAGE_H_
-
-
-typedef enum {
- NO_EXTRA_TEXT = 0,
- SHORT_COMMENT,
- LONG_COMMENT,
- COPY_TEXT,
- WHITESPACE
-} which_extra_text;
-
-typedef struct extra_text_type {
- unsigned lineno;
- which_extra_text which;
- char* data;
- unsigned len;
- struct extra_text_type* next;
-} extra_text_type;
-
-typedef struct buffer_type {
- unsigned lineno;
- unsigned token;
- char *data;
- extra_text_type* extra;
-} buffer_type;
-
-typedef struct type_type {
- buffer_type type;
- buffer_type array_token;
- int dimension;
-} type_type;
-
-typedef struct arg_type {
- buffer_type comma_token; // empty in the first one in the list
- buffer_type direction;
- type_type type;
- buffer_type name;
- struct arg_type *next;
-} arg_type;
-
-enum {
- METHOD_TYPE
-};
-
-typedef struct interface_item_type {
- unsigned item_type;
- struct interface_item_type* next;
-} interface_item_type;
-
-typedef struct method_type {
- interface_item_type interface_item;
- type_type type;
- bool oneway;
- buffer_type oneway_token;
- buffer_type name;
- buffer_type open_paren_token;
- arg_type* args;
- buffer_type close_paren_token;
- bool hasId;
- buffer_type equals_token;
- buffer_type id;
- // XXX missing comments/copy text here
- buffer_type semicolon_token;
- buffer_type* comments_token; // points into this structure, DO NOT DELETE
- int assigned_id;
-} method_type;
-
-enum {
- USER_DATA_TYPE = 12,
- INTERFACE_TYPE_BINDER
-};
-
-typedef struct document_item_type {
- unsigned item_type;
- struct document_item_type* next;
-} document_item_type;
-
-
-typedef struct user_data_type {
- document_item_type document_item;
- buffer_type keyword_token; // only the first one
- char* package;
- buffer_type name;
- buffer_type semicolon_token;
- bool parcelable;
-} user_data_type;
-
-typedef struct interface_type {
- document_item_type document_item;
- buffer_type interface_token;
- bool oneway;
- buffer_type oneway_token;
- char* package;
- buffer_type name;
- buffer_type open_brace_token;
- interface_item_type* interface_items;
- buffer_type close_brace_token;
- buffer_type* comments_token; // points into this structure, DO NOT DELETE
-} interface_type;
-
-typedef union lexer_type {
- buffer_type buffer;
- type_type type;
- arg_type *arg;
- method_type* method;
- interface_item_type* interface_item;
- interface_type* interface_obj;
- user_data_type* user_data;
- document_item_type* document_item;
-} lexer_type;
-
-
-#define YYSTYPE lexer_type
-
-#if __cplusplus
-extern "C" {
-#endif
-
-int parse_aidl(char const *);
-
-// strips off the leading whitespace, the "import" text
-// also returns whether it's a local or system import
-// we rely on the input matching the import regex from below
-char* parse_import_statement(const char* text);
-
-// in, out or inout
-enum {
- IN_PARAMETER = 1,
- OUT_PARAMETER = 2,
- INOUT_PARAMETER = 3
-};
-int convert_direction(const char* direction);
-
-// callbacks from within the parser
-// these functions all take ownership of the strings
-typedef struct ParserCallbacks {
- void (*document)(document_item_type* items);
- void (*import)(buffer_type* statement);
-} ParserCallbacks;
-
-extern ParserCallbacks* g_callbacks;
-
-// true if there was an error parsing, false otherwise
-extern int g_error;
-
-// the name of the file we're currently parsing
-extern char const* g_currentFilename;
-
-// the package name for our current file
-extern char const* g_currentPackage;
-
-typedef enum {
- STATEMENT_INSIDE_INTERFACE
-} error_type;
-
-void init_buffer_type(buffer_type* buf, int lineno);
-
-
-#if __cplusplus
-}
-#endif
-
-
-#endif // AIDL_AIDL_LANGUAGE_H_
diff --git a/tools/aidl/aidl_language_l.l b/tools/aidl/aidl_language_l.l
deleted file mode 100644
index aa42f2e..0000000
--- a/tools/aidl/aidl_language_l.l
+++ /dev/null
@@ -1,212 +0,0 @@
-%{
-#include "aidl_language.h"
-#include "aidl_language_y.h"
-#include "search_path.h"
-#include <string.h>
-#include <stdlib.h>
-
-extern YYSTYPE yylval;
-
-// comment and whitespace handling
-// these functions save a copy of the buffer
-static void begin_extra_text(unsigned lineno, which_extra_text which);
-static void append_extra_text(char* text);
-static extra_text_type* get_extra_text(void); // you now own the object
- // this returns
-static void drop_extra_text(void);
-
-// package handling
-static void do_package_statement(const char* importText);
-
-#define SET_BUFFER(t) \
- do { \
- yylval.buffer.lineno = yylineno; \
- yylval.buffer.token = (t); \
- yylval.buffer.data = strdup(yytext); \
- yylval.buffer.extra = get_extra_text(); \
- } while(0)
-
-%}
-
-%option yylineno
-%option noyywrap
-
-%x COPYING LONG_COMMENT
-
-identifier [_a-zA-Z][_a-zA-Z0-9\.]*
-whitespace ([ \t\n\r]+)
-brackets \[{whitespace}?\]
-idvalue (0|[1-9][0-9]*)
-
-%%
-
-
-\%\%\{ { begin_extra_text(yylineno, COPY_TEXT); BEGIN(COPYING); }
-<COPYING>\}\%\% { BEGIN(INITIAL); }
-<COPYING>.*\n { append_extra_text(yytext); }
-<COPYING>.* { append_extra_text(yytext); }
-<COPYING>\n+ { append_extra_text(yytext); }
-
-
-\/\* { begin_extra_text(yylineno, (which_extra_text)LONG_COMMENT);
- BEGIN(LONG_COMMENT); }
-<LONG_COMMENT>[^*]* { append_extra_text(yytext); }
-<LONG_COMMENT>\*+[^/] { append_extra_text(yytext); }
-<LONG_COMMENT>\n { append_extra_text(yytext); }
-<LONG_COMMENT>\**\/ { BEGIN(INITIAL); }
-
-^{whitespace}?import{whitespace}[^ \t\r\n]+{whitespace}?; {
- SET_BUFFER(IMPORT);
- return IMPORT;
- }
-^{whitespace}?package{whitespace}[^ \t\r\n]+{whitespace}?; {
- do_package_statement(yytext);
- SET_BUFFER(PACKAGE);
- return PACKAGE;
- }
-<<EOF>> { yyterminate(); }
-
-\/\/.*\n { begin_extra_text(yylineno, SHORT_COMMENT);
- append_extra_text(yytext); }
-
-{whitespace} { /* begin_extra_text(yylineno, WHITESPACE);
- append_extra_text(yytext); */ }
-
-; { SET_BUFFER(';'); return ';'; }
-\{ { SET_BUFFER('{'); return '{'; }
-\} { SET_BUFFER('}'); return '}'; }
-\( { SET_BUFFER('('); return '('; }
-\) { SET_BUFFER(')'); return ')'; }
-, { SET_BUFFER(','); return ','; }
-= { SET_BUFFER('='); return '='; }
-
- /* keywords */
-parcelable { SET_BUFFER(PARCELABLE); return PARCELABLE; }
-interface { SET_BUFFER(INTERFACE); return INTERFACE; }
-in { SET_BUFFER(IN); return IN; }
-out { SET_BUFFER(OUT); return OUT; }
-inout { SET_BUFFER(INOUT); return INOUT; }
-oneway { SET_BUFFER(ONEWAY); return ONEWAY; }
-
-{brackets}+ { SET_BUFFER(ARRAY); return ARRAY; }
-{idvalue} { SET_BUFFER(IDVALUE); return IDVALUE; }
-{identifier} { SET_BUFFER(IDENTIFIER); return IDENTIFIER; }
-{identifier}\<{whitespace}*{identifier}({whitespace}*,{whitespace}*{identifier})*{whitespace}*\> {
- SET_BUFFER(GENERIC); return GENERIC; }
-
- /* syntax error! */
-. { printf("UNKNOWN(%s)", yytext);
- yylval.buffer.lineno = yylineno;
- yylval.buffer.token = IDENTIFIER;
- yylval.buffer.data = strdup(yytext);
- return IDENTIFIER;
- }
-
-%%
-
-// comment and whitespace handling
-// ================================================
-extra_text_type* g_extraText = NULL;
-extra_text_type* g_nextExtraText = NULL;
-
-void begin_extra_text(unsigned lineno, which_extra_text which)
-{
- extra_text_type* text = (extra_text_type*)malloc(sizeof(extra_text_type));
- text->lineno = lineno;
- text->which = which;
- text->data = NULL;
- text->len = 0;
- text->next = NULL;
- if (g_nextExtraText == NULL) {
- g_extraText = text;
- } else {
- g_nextExtraText->next = text;
- }
- g_nextExtraText = text;
-}
-
-void append_extra_text(char* text)
-{
- if (g_nextExtraText->data == NULL) {
- g_nextExtraText->data = strdup(text);
- g_nextExtraText->len = strlen(text);
- } else {
- char* orig = g_nextExtraText->data;
- unsigned oldLen = g_nextExtraText->len;
- unsigned len = strlen(text);
- g_nextExtraText->len += len;
- g_nextExtraText->data = (char*)malloc(g_nextExtraText->len+1);
- memcpy(g_nextExtraText->data, orig, oldLen);
- memcpy(g_nextExtraText->data+oldLen, text, len);
- g_nextExtraText->data[g_nextExtraText->len] = '\0';
- free(orig);
- }
-}
-
-extra_text_type*
-get_extra_text(void)
-{
- extra_text_type* result = g_extraText;
- g_extraText = NULL;
- g_nextExtraText = NULL;
- return result;
-}
-
-void drop_extra_text(void)
-{
- extra_text_type* p = g_extraText;
- while (p) {
- extra_text_type* next = p->next;
- free(p->data);
- free(p);
- free(next);
- }
- g_extraText = NULL;
- g_nextExtraText = NULL;
-}
-
-
-// package handling
-// ================================================
-void do_package_statement(const char* importText)
-{
- if (g_currentPackage) free((void*)g_currentPackage);
- g_currentPackage = parse_import_statement(importText);
-}
-
-
-// main parse function
-// ================================================
-char const* g_currentFilename = NULL;
-char const* g_currentPackage = NULL;
-
-int yyparse(void);
-
-int parse_aidl(char const *filename)
-{
- yyin = fopen(filename, "r");
- if (yyin) {
- char const* oldFilename = g_currentFilename;
- char const* oldPackage = g_currentPackage;
- g_currentFilename = strdup(filename);
-
- g_error = 0;
- yylineno = 1;
- int rv = yyparse();
- if (g_error != 0) {
- rv = g_error;
- }
-
- free((void*)g_currentFilename);
- g_currentFilename = oldFilename;
-
- if (g_currentPackage) free((void*)g_currentPackage);
- g_currentPackage = oldPackage;
-
- return rv;
- } else {
- fprintf(stderr, "aidl: unable to open file for read: %s\n", filename);
- return 1;
- }
-}
-
diff --git a/tools/aidl/aidl_language_y.y b/tools/aidl/aidl_language_y.y
deleted file mode 100644
index 9c5d10e..0000000
--- a/tools/aidl/aidl_language_y.y
+++ /dev/null
@@ -1,334 +0,0 @@
-%{
-#include "aidl_language.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-int yyerror(char* errstr);
-int yylex(void);
-extern int yylineno;
-
-static int count_brackets(const char*);
-
-%}
-
-%token IMPORT
-%token PACKAGE
-%token IDENTIFIER
-%token IDVALUE
-%token GENERIC
-%token ARRAY
-%token PARCELABLE
-%token INTERFACE
-%token IN
-%token OUT
-%token INOUT
-%token ONEWAY
-
-%%
-document:
- document_items { g_callbacks->document($1.document_item); }
- | headers document_items { g_callbacks->document($2.document_item); }
- ;
-
-headers:
- package { }
- | imports { }
- | package imports { }
- ;
-
-package:
- PACKAGE { }
- ;
-
-imports:
- IMPORT { g_callbacks->import(&($1.buffer)); }
- | IMPORT imports { g_callbacks->import(&($1.buffer)); }
- ;
-
-document_items:
- { $$.document_item = NULL; }
- | document_items declaration {
- if ($2.document_item == NULL) {
- // error cases only
- $$ = $1;
- } else {
- document_item_type* p = $1.document_item;
- while (p && p->next) {
- p=p->next;
- }
- if (p) {
- p->next = (document_item_type*)$2.document_item;
- $$ = $1;
- } else {
- $$.document_item = (document_item_type*)$2.document_item;
- }
- }
- }
- | document_items error {
- fprintf(stderr, "%s:%d: syntax error don't know what to do with \"%s\"\n", g_currentFilename,
- $2.buffer.lineno, $2.buffer.data);
- $$ = $1;
- }
- ;
-
-declaration:
- parcelable_decl { $$.document_item = (document_item_type*)$1.user_data; }
- | interface_decl { $$.document_item = (document_item_type*)$1.interface_item; }
- ;
-
-parcelable_decl:
- PARCELABLE IDENTIFIER ';' {
- user_data_type* b = (user_data_type*)malloc(sizeof(user_data_type));
- b->document_item.item_type = USER_DATA_TYPE;
- b->document_item.next = NULL;
- b->keyword_token = $1.buffer;
- b->name = $2.buffer;
- b->package = g_currentPackage ? strdup(g_currentPackage) : NULL;
- b->semicolon_token = $3.buffer;
- b->parcelable = true;
- $$.user_data = b;
- }
- | PARCELABLE ';' {
- fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name.\n",
- g_currentFilename, $1.buffer.lineno);
- $$.user_data = NULL;
- }
- | PARCELABLE error ';' {
- fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name, saw \"%s\".\n",
- g_currentFilename, $2.buffer.lineno, $2.buffer.data);
- $$.user_data = NULL;
- }
- ;
-
-interface_header:
- INTERFACE {
- interface_type* c = (interface_type*)malloc(sizeof(interface_type));
- c->document_item.item_type = INTERFACE_TYPE_BINDER;
- c->document_item.next = NULL;
- c->interface_token = $1.buffer;
- c->oneway = false;
- memset(&c->oneway_token, 0, sizeof(buffer_type));
- c->comments_token = &c->interface_token;
- $$.interface_obj = c;
- }
- | ONEWAY INTERFACE {
- interface_type* c = (interface_type*)malloc(sizeof(interface_type));
- c->document_item.item_type = INTERFACE_TYPE_BINDER;
- c->document_item.next = NULL;
- c->interface_token = $2.buffer;
- c->oneway = true;
- c->oneway_token = $1.buffer;
- c->comments_token = &c->oneway_token;
- $$.interface_obj = c;
- }
- ;
-
-interface_decl:
- interface_header IDENTIFIER '{' interface_items '}' {
- interface_type* c = $1.interface_obj;
- c->name = $2.buffer;
- c->package = g_currentPackage ? strdup(g_currentPackage) : NULL;
- c->open_brace_token = $3.buffer;
- c->interface_items = $4.interface_item;
- c->close_brace_token = $5.buffer;
- $$.interface_obj = c;
- }
- | INTERFACE error '{' interface_items '}' {
- fprintf(stderr, "%s:%d: syntax error in interface declaration. Expected type name, saw \"%s\"\n",
- g_currentFilename, $2.buffer.lineno, $2.buffer.data);
- $$.document_item = NULL;
- }
- | INTERFACE error '}' {
- fprintf(stderr, "%s:%d: syntax error in interface declaration. Expected type name, saw \"%s\"\n",
- g_currentFilename, $2.buffer.lineno, $2.buffer.data);
- $$.document_item = NULL;
- }
-
- ;
-
-interface_items:
- { $$.interface_item = NULL; }
- | interface_items method_decl {
- interface_item_type* p=$1.interface_item;
- while (p && p->next) {
- p=p->next;
- }
- if (p) {
- p->next = (interface_item_type*)$2.method;
- $$ = $1;
- } else {
- $$.interface_item = (interface_item_type*)$2.method;
- }
- }
- | interface_items error ';' {
- fprintf(stderr, "%s:%d: syntax error before ';' (expected method declaration)\n",
- g_currentFilename, $3.buffer.lineno);
- $$ = $1;
- }
- ;
-
-method_decl:
- type IDENTIFIER '(' arg_list ')' ';' {
- method_type *method = (method_type*)malloc(sizeof(method_type));
- method->interface_item.item_type = METHOD_TYPE;
- method->interface_item.next = NULL;
- method->oneway = false;
- method->type = $1.type;
- memset(&method->oneway_token, 0, sizeof(buffer_type));
- method->name = $2.buffer;
- method->open_paren_token = $3.buffer;
- method->args = $4.arg;
- method->close_paren_token = $5.buffer;
- method->hasId = false;
- memset(&method->equals_token, 0, sizeof(buffer_type));
- memset(&method->id, 0, sizeof(buffer_type));
- method->semicolon_token = $6.buffer;
- method->comments_token = &method->type.type;
- $$.method = method;
- }
- | ONEWAY type IDENTIFIER '(' arg_list ')' ';' {
- method_type *method = (method_type*)malloc(sizeof(method_type));
- method->interface_item.item_type = METHOD_TYPE;
- method->interface_item.next = NULL;
- method->oneway = true;
- method->oneway_token = $1.buffer;
- method->type = $2.type;
- method->name = $3.buffer;
- method->open_paren_token = $4.buffer;
- method->args = $5.arg;
- method->close_paren_token = $6.buffer;
- method->hasId = false;
- memset(&method->equals_token, 0, sizeof(buffer_type));
- memset(&method->id, 0, sizeof(buffer_type));
- method->semicolon_token = $7.buffer;
- method->comments_token = &method->oneway_token;
- $$.method = method;
- }
- | type IDENTIFIER '(' arg_list ')' '=' IDVALUE ';' {
- method_type *method = (method_type*)malloc(sizeof(method_type));
- method->interface_item.item_type = METHOD_TYPE;
- method->interface_item.next = NULL;
- method->oneway = false;
- memset(&method->oneway_token, 0, sizeof(buffer_type));
- method->type = $1.type;
- method->name = $2.buffer;
- method->open_paren_token = $3.buffer;
- method->args = $4.arg;
- method->close_paren_token = $5.buffer;
- method->hasId = true;
- method->equals_token = $6.buffer;
- method->id = $7.buffer;
- method->semicolon_token = $8.buffer;
- method->comments_token = &method->type.type;
- $$.method = method;
- }
- | ONEWAY type IDENTIFIER '(' arg_list ')' '=' IDVALUE ';' {
- method_type *method = (method_type*)malloc(sizeof(method_type));
- method->interface_item.item_type = METHOD_TYPE;
- method->interface_item.next = NULL;
- method->oneway = true;
- method->oneway_token = $1.buffer;
- method->type = $2.type;
- method->name = $3.buffer;
- method->open_paren_token = $4.buffer;
- method->args = $5.arg;
- method->close_paren_token = $6.buffer;
- method->hasId = true;
- method->equals_token = $7.buffer;
- method->id = $8.buffer;
- method->semicolon_token = $9.buffer;
- method->comments_token = &method->oneway_token;
- $$.method = method;
- }
- ;
-
-arg_list:
- { $$.arg = NULL; }
- | arg { $$ = $1; }
- | arg_list ',' arg {
- if ($$.arg != NULL) {
- // only NULL on error
- $$ = $1;
- arg_type *p = $1.arg;
- while (p && p->next) {
- p=p->next;
- }
- $3.arg->comma_token = $2.buffer;
- p->next = $3.arg;
- }
- }
- | error {
- fprintf(stderr, "%s:%d: syntax error in parameter list\n", g_currentFilename, $1.buffer.lineno);
- $$.arg = NULL;
- }
- ;
-
-arg:
- direction type IDENTIFIER {
- arg_type* arg = (arg_type*)malloc(sizeof(arg_type));
- memset(&arg->comma_token, 0, sizeof(buffer_type));
- arg->direction = $1.buffer;
- arg->type = $2.type;
- arg->name = $3.buffer;
- arg->next = NULL;
- $$.arg = arg;
- }
- ;
-
-type:
- IDENTIFIER {
- $$.type.type = $1.buffer;
- init_buffer_type(&$$.type.array_token, yylineno);
- $$.type.dimension = 0;
- }
- | IDENTIFIER ARRAY {
- $$.type.type = $1.buffer;
- $$.type.array_token = $2.buffer;
- $$.type.dimension = count_brackets($2.buffer.data);
- }
- | GENERIC {
- $$.type.type = $1.buffer;
- init_buffer_type(&$$.type.array_token, yylineno);
- $$.type.dimension = 0;
- }
- ;
-
-direction:
- { init_buffer_type(&$$.buffer, yylineno); }
- | IN { $$.buffer = $1.buffer; }
- | OUT { $$.buffer = $1.buffer; }
- | INOUT { $$.buffer = $1.buffer; }
- ;
-
-%%
-
-#include <ctype.h>
-#include <stdio.h>
-
-int g_error = 0;
-
-int yyerror(char* errstr)
-{
- fprintf(stderr, "%s:%d: %s\n", g_currentFilename, yylineno, errstr);
- g_error = 1;
- return 1;
-}
-
-void init_buffer_type(buffer_type* buf, int lineno)
-{
- buf->lineno = lineno;
- buf->token = 0;
- buf->data = NULL;
- buf->extra = NULL;
-}
-
-static int count_brackets(const char* s)
-{
- int n=0;
- while (*s) {
- if (*s == '[') n++;
- s++;
- }
- return n;
-}
diff --git a/tools/aidl/generate_java.cpp b/tools/aidl/generate_java.cpp
deleted file mode 100644
index 1509340..0000000
--- a/tools/aidl/generate_java.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-#include "generate_java.h"
-#include "Type.h"
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-// =================================================
-VariableFactory::VariableFactory(const string& base)
- :m_base(base),
- m_index(0)
-{
-}
-
-Variable*
-VariableFactory::Get(Type* type)
-{
- char name[100];
- sprintf(name, "%s%d", m_base.c_str(), m_index);
- m_index++;
- Variable* v = new Variable(type, name);
- m_vars.push_back(v);
- return v;
-}
-
-Variable*
-VariableFactory::Get(int index)
-{
- return m_vars[index];
-}
-
-// =================================================
-string
-gather_comments(extra_text_type* extra)
-{
- string s;
- while (extra) {
- if (extra->which == SHORT_COMMENT) {
- s += extra->data;
- }
- else if (extra->which == LONG_COMMENT) {
- s += "/*";
- s += extra->data;
- s += "*/";
- }
- extra = extra->next;
- }
- return s;
-}
-
-string
-append(const char* a, const char* b)
-{
- string s = a;
- s += b;
- return s;
-}
-
-// =================================================
-int
-generate_java(const string& filename, const string& originalSrc,
- interface_type* iface)
-{
- Class* cl;
-
- if (iface->document_item.item_type == INTERFACE_TYPE_BINDER) {
- cl = generate_binder_interface_class(iface);
- }
-
- Document* document = new Document;
- document->comment = "";
- if (iface->package) document->package = iface->package;
- document->originalSrc = originalSrc;
- document->classes.push_back(cl);
-
-// printf("outputting... filename=%s\n", filename.c_str());
- FILE* to;
- if (filename == "-") {
- to = stdout;
- } else {
- /* open file in binary mode to ensure that the tool produces the
- * same output on all platforms !!
- */
- to = fopen(filename.c_str(), "wb");
- if (to == NULL) {
- fprintf(stderr, "unable to open %s for write\n", filename.c_str());
- return 1;
- }
- }
-
- document->Write(to);
-
- fclose(to);
- return 0;
-}
-
diff --git a/tools/aidl/generate_java.h b/tools/aidl/generate_java.h
deleted file mode 100644
index 4e79743..0000000
--- a/tools/aidl/generate_java.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef AIDL_GENERATE_JAVA_H_
-#define AIDL_GENERATE_JAVA_H_
-
-#include "aidl_language.h"
-#include "AST.h"
-
-#include <string>
-
-using namespace std;
-
-int generate_java(const string& filename, const string& originalSrc,
- interface_type* iface);
-
-Class* generate_binder_interface_class(const interface_type* iface);
-
-string gather_comments(extra_text_type* extra);
-string append(const char* a, const char* b);
-
-class VariableFactory
-{
-public:
- VariableFactory(const string& base); // base must be short
- Variable* Get(Type* type);
- Variable* Get(int index);
-private:
- vector<Variable*> m_vars;
- string m_base;
- int m_index;
-};
-
-#endif // AIDL_GENERATE_JAVA_H_
-
diff --git a/tools/aidl/generate_java_binder.cpp b/tools/aidl/generate_java_binder.cpp
deleted file mode 100644
index f291ceb..0000000
--- a/tools/aidl/generate_java_binder.cpp
+++ /dev/null
@@ -1,560 +0,0 @@
-#include "generate_java.h"
-#include "Type.h"
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-// =================================================
-class StubClass : public Class
-{
-public:
- StubClass(Type* type, Type* interfaceType);
- virtual ~StubClass();
-
- Variable* transact_code;
- Variable* transact_data;
- Variable* transact_reply;
- Variable* transact_flags;
- SwitchStatement* transact_switch;
-private:
- void make_as_interface(Type* interfaceType);
-};
-
-StubClass::StubClass(Type* type, Type* interfaceType)
- :Class()
-{
- this->comment = "/** Local-side IPC implementation stub class. */";
- this->modifiers = PUBLIC | ABSTRACT | STATIC;
- this->what = Class::CLASS;
- this->type = type;
- this->extends = BINDER_NATIVE_TYPE;
- this->interfaces.push_back(interfaceType);
-
- // descriptor
- Field* descriptor = new Field(STATIC | FINAL | PRIVATE,
- new Variable(STRING_TYPE, "DESCRIPTOR"));
- descriptor->value = "\"" + interfaceType->QualifiedName() + "\"";
- this->elements.push_back(descriptor);
-
- // ctor
- Method* ctor = new Method;
- ctor->modifiers = PUBLIC;
- ctor->comment = "/** Construct the stub at attach it to the "
- "interface. */";
- ctor->name = "Stub";
- ctor->statements = new StatementBlock;
- MethodCall* attach = new MethodCall(THIS_VALUE, "attachInterface",
- 2, THIS_VALUE, new LiteralExpression("DESCRIPTOR"));
- ctor->statements->Add(attach);
- this->elements.push_back(ctor);
-
- // asInterface
- make_as_interface(interfaceType);
-
- // asBinder
- Method* asBinder = new Method;
- asBinder->modifiers = PUBLIC | OVERRIDE;
- asBinder->returnType = IBINDER_TYPE;
- asBinder->name = "asBinder";
- asBinder->statements = new StatementBlock;
- asBinder->statements->Add(new ReturnStatement(THIS_VALUE));
- this->elements.push_back(asBinder);
-
- // onTransact
- this->transact_code = new Variable(INT_TYPE, "code");
- this->transact_data = new Variable(PARCEL_TYPE, "data");
- this->transact_reply = new Variable(PARCEL_TYPE, "reply");
- this->transact_flags = new Variable(INT_TYPE, "flags");
- Method* onTransact = new Method;
- onTransact->modifiers = PUBLIC | OVERRIDE;
- onTransact->returnType = BOOLEAN_TYPE;
- onTransact->name = "onTransact";
- onTransact->parameters.push_back(this->transact_code);
- onTransact->parameters.push_back(this->transact_data);
- onTransact->parameters.push_back(this->transact_reply);
- onTransact->parameters.push_back(this->transact_flags);
- onTransact->statements = new StatementBlock;
- onTransact->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
- this->elements.push_back(onTransact);
- this->transact_switch = new SwitchStatement(this->transact_code);
-
- onTransact->statements->Add(this->transact_switch);
- MethodCall* superCall = new MethodCall(SUPER_VALUE, "onTransact", 4,
- this->transact_code, this->transact_data,
- this->transact_reply, this->transact_flags);
- onTransact->statements->Add(new ReturnStatement(superCall));
-}
-
-StubClass::~StubClass()
-{
-}
-
-void
-StubClass::make_as_interface(Type *interfaceType)
-{
- Variable* obj = new Variable(IBINDER_TYPE, "obj");
-
- Method* m = new Method;
- m->comment = "/**\n * Cast an IBinder object into an ";
- m->comment += interfaceType->QualifiedName();
- m->comment += " interface,\n";
- m->comment += " * generating a proxy if needed.\n */";
- m->modifiers = PUBLIC | STATIC;
- m->returnType = interfaceType;
- m->name = "asInterface";
- m->parameters.push_back(obj);
- m->statements = new StatementBlock;
-
- IfStatement* ifstatement = new IfStatement();
- ifstatement->expression = new Comparison(obj, "==", NULL_VALUE);
- ifstatement->statements = new StatementBlock;
- ifstatement->statements->Add(new ReturnStatement(NULL_VALUE));
- m->statements->Add(ifstatement);
-
- // IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
- MethodCall* queryLocalInterface = new MethodCall(obj, "queryLocalInterface");
- queryLocalInterface->arguments.push_back(new LiteralExpression("DESCRIPTOR"));
- IInterfaceType* iinType = new IInterfaceType();
- Variable *iin = new Variable(iinType, "iin");
- VariableDeclaration* iinVd = new VariableDeclaration(iin, queryLocalInterface, NULL);
- m->statements->Add(iinVd);
-
- // Ensure the instance type of the local object is as expected.
- // One scenario where this is needed is if another package (with a
- // different class loader) runs in the same process as the service.
-
- // if (iin != null && iin instanceof <interfaceType>) return (<interfaceType>) iin;
- Comparison* iinNotNull = new Comparison(iin, "!=", NULL_VALUE);
- Comparison* instOfCheck = new Comparison(iin, " instanceof ",
- new LiteralExpression(interfaceType->QualifiedName()));
- IfStatement* instOfStatement = new IfStatement();
- instOfStatement->expression = new Comparison(iinNotNull, "&&", instOfCheck);
- instOfStatement->statements = new StatementBlock;
- instOfStatement->statements->Add(new ReturnStatement(new Cast(interfaceType, iin)));
- m->statements->Add(instOfStatement);
-
- string proxyType = interfaceType->QualifiedName();
- proxyType += ".Stub.Proxy";
- NewExpression* ne = new NewExpression(NAMES.Find(proxyType));
- ne->arguments.push_back(obj);
- m->statements->Add(new ReturnStatement(ne));
-
- this->elements.push_back(m);
-}
-
-
-
-// =================================================
-class ProxyClass : public Class
-{
-public:
- ProxyClass(Type* type, InterfaceType* interfaceType);
- virtual ~ProxyClass();
-
- Variable* mRemote;
- bool mOneWay;
-};
-
-ProxyClass::ProxyClass(Type* type, InterfaceType* interfaceType)
- :Class()
-{
- this->modifiers = PRIVATE | STATIC;
- this->what = Class::CLASS;
- this->type = type;
- this->interfaces.push_back(interfaceType);
-
- mOneWay = interfaceType->OneWay();
-
- // IBinder mRemote
- mRemote = new Variable(IBINDER_TYPE, "mRemote");
- this->elements.push_back(new Field(PRIVATE, mRemote));
-
- // Proxy()
- Variable* remote = new Variable(IBINDER_TYPE, "remote");
- Method* ctor = new Method;
- ctor->name = "Proxy";
- ctor->statements = new StatementBlock;
- ctor->parameters.push_back(remote);
- ctor->statements->Add(new Assignment(mRemote, remote));
- this->elements.push_back(ctor);
-
- // IBinder asBinder()
- Method* asBinder = new Method;
- asBinder->modifiers = PUBLIC | OVERRIDE;
- asBinder->returnType = IBINDER_TYPE;
- asBinder->name = "asBinder";
- asBinder->statements = new StatementBlock;
- asBinder->statements->Add(new ReturnStatement(mRemote));
- this->elements.push_back(asBinder);
-}
-
-ProxyClass::~ProxyClass()
-{
-}
-
-// =================================================
-static void
-generate_new_array(Type* t, StatementBlock* addTo, Variable* v,
- Variable* parcel)
-{
- Variable* len = new Variable(INT_TYPE, v->name + "_length");
- addTo->Add(new VariableDeclaration(len, new MethodCall(parcel, "readInt")));
- IfStatement* lencheck = new IfStatement();
- lencheck->expression = new Comparison(len, "<", new LiteralExpression("0"));
- lencheck->statements->Add(new Assignment(v, NULL_VALUE));
- lencheck->elseif = new IfStatement();
- lencheck->elseif->statements->Add(new Assignment(v,
- new NewArrayExpression(t, len)));
- addTo->Add(lencheck);
-}
-
-static void
-generate_write_to_parcel(Type* t, StatementBlock* addTo, Variable* v,
- Variable* parcel, int flags)
-{
- if (v->dimension == 0) {
- t->WriteToParcel(addTo, v, parcel, flags);
- }
- if (v->dimension == 1) {
- t->WriteArrayToParcel(addTo, v, parcel, flags);
- }
-}
-
-static void
-generate_create_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl)
-{
- if (v->dimension == 0) {
- t->CreateFromParcel(addTo, v, parcel, cl);
- }
- if (v->dimension == 1) {
- t->CreateArrayFromParcel(addTo, v, parcel, cl);
- }
-}
-
-static void
-generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
- Variable* parcel, Variable** cl)
-{
- if (v->dimension == 0) {
- t->ReadFromParcel(addTo, v, parcel, cl);
- }
- if (v->dimension == 1) {
- t->ReadArrayFromParcel(addTo, v, parcel, cl);
- }
-}
-
-
-static void
-generate_method(const method_type* method, Class* interface,
- StubClass* stubClass, ProxyClass* proxyClass, int index)
-{
- arg_type* arg;
- int i;
- bool hasOutParams = false;
-
- const bool oneway = proxyClass->mOneWay || method->oneway;
-
- // == the TRANSACT_ constant =============================================
- string transactCodeName = "TRANSACTION_";
- transactCodeName += method->name.data;
-
- char transactCodeValue[60];
- sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
-
- Field* transactCode = new Field(STATIC | FINAL,
- new Variable(INT_TYPE, transactCodeName));
- transactCode->value = transactCodeValue;
- stubClass->elements.push_back(transactCode);
-
- // == the declaration in the interface ===================================
- Method* decl = new Method;
- decl->comment = gather_comments(method->comments_token->extra);
- decl->modifiers = PUBLIC;
- decl->returnType = NAMES.Search(method->type.type.data);
- decl->returnTypeDimension = method->type.dimension;
- decl->name = method->name.data;
-
- arg = method->args;
- while (arg != NULL) {
- decl->parameters.push_back(new Variable(
- NAMES.Search(arg->type.type.data), arg->name.data,
- arg->type.dimension));
- arg = arg->next;
- }
-
- decl->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
-
- interface->elements.push_back(decl);
-
- // == the stub method ====================================================
-
- Case* c = new Case(transactCodeName);
-
- MethodCall* realCall = new MethodCall(THIS_VALUE, method->name.data);
-
- // interface token validation is the very first thing we do
- c->statements->Add(new MethodCall(stubClass->transact_data,
- "enforceInterface", 1, new LiteralExpression("DESCRIPTOR")));
-
- // args
- Variable* cl = NULL;
- VariableFactory stubArgs("_arg");
- arg = method->args;
- while (arg != NULL) {
- Type* t = NAMES.Search(arg->type.type.data);
- Variable* v = stubArgs.Get(t);
- v->dimension = arg->type.dimension;
-
- c->statements->Add(new VariableDeclaration(v));
-
- if (convert_direction(arg->direction.data) & IN_PARAMETER) {
- generate_create_from_parcel(t, c->statements, v,
- stubClass->transact_data, &cl);
- } else {
- if (arg->type.dimension == 0) {
- c->statements->Add(new Assignment(v, new NewExpression(v->type)));
- }
- else if (arg->type.dimension == 1) {
- generate_new_array(v->type, c->statements, v,
- stubClass->transact_data);
- }
- else {
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__,
- __LINE__);
- }
- }
-
- realCall->arguments.push_back(v);
-
- arg = arg->next;
- }
-
- // the real call
- Variable* _result = NULL;
- if (0 == strcmp(method->type.type.data, "void")) {
- c->statements->Add(realCall);
-
- if (!oneway) {
- // report that there were no exceptions
- MethodCall* ex = new MethodCall(stubClass->transact_reply,
- "writeNoException", 0);
- c->statements->Add(ex);
- }
- } else {
- _result = new Variable(decl->returnType, "_result",
- decl->returnTypeDimension);
- c->statements->Add(new VariableDeclaration(_result, realCall));
-
- if (!oneway) {
- // report that there were no exceptions
- MethodCall* ex = new MethodCall(stubClass->transact_reply,
- "writeNoException", 0);
- c->statements->Add(ex);
- }
-
- // marshall the return value
- generate_write_to_parcel(decl->returnType, c->statements, _result,
- stubClass->transact_reply,
- Type::PARCELABLE_WRITE_RETURN_VALUE);
- }
-
- // out parameters
- i = 0;
- arg = method->args;
- while (arg != NULL) {
- Type* t = NAMES.Search(arg->type.type.data);
- Variable* v = stubArgs.Get(i++);
-
- if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
- generate_write_to_parcel(t, c->statements, v,
- stubClass->transact_reply,
- Type::PARCELABLE_WRITE_RETURN_VALUE);
- hasOutParams = true;
- }
-
- arg = arg->next;
- }
-
- // return true
- c->statements->Add(new ReturnStatement(TRUE_VALUE));
- stubClass->transact_switch->cases.push_back(c);
-
- // == the proxy method ===================================================
- Method* proxy = new Method;
- proxy->comment = gather_comments(method->comments_token->extra);
- proxy->modifiers = PUBLIC | OVERRIDE;
- proxy->returnType = NAMES.Search(method->type.type.data);
- proxy->returnTypeDimension = method->type.dimension;
- proxy->name = method->name.data;
- proxy->statements = new StatementBlock;
- arg = method->args;
- while (arg != NULL) {
- proxy->parameters.push_back(new Variable(
- NAMES.Search(arg->type.type.data), arg->name.data,
- arg->type.dimension));
- arg = arg->next;
- }
- proxy->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
- proxyClass->elements.push_back(proxy);
-
- // the parcels
- Variable* _data = new Variable(PARCEL_TYPE, "_data");
- proxy->statements->Add(new VariableDeclaration(_data,
- new MethodCall(PARCEL_TYPE, "obtain")));
- Variable* _reply = NULL;
- if (!oneway) {
- _reply = new Variable(PARCEL_TYPE, "_reply");
- proxy->statements->Add(new VariableDeclaration(_reply,
- new MethodCall(PARCEL_TYPE, "obtain")));
- }
-
- // the return value
- _result = NULL;
- if (0 != strcmp(method->type.type.data, "void")) {
- _result = new Variable(proxy->returnType, "_result",
- method->type.dimension);
- proxy->statements->Add(new VariableDeclaration(_result));
- }
-
- // try and finally
- TryStatement* tryStatement = new TryStatement();
- proxy->statements->Add(tryStatement);
- FinallyStatement* finallyStatement = new FinallyStatement();
- proxy->statements->Add(finallyStatement);
-
- // the interface identifier token: the DESCRIPTOR constant, marshalled as a string
- tryStatement->statements->Add(new MethodCall(_data, "writeInterfaceToken",
- 1, new LiteralExpression("DESCRIPTOR")));
-
- // the parameters
- arg = method->args;
- while (arg != NULL) {
- Type* t = NAMES.Search(arg->type.type.data);
- Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
- int dir = convert_direction(arg->direction.data);
- if (dir == OUT_PARAMETER && arg->type.dimension != 0) {
- IfStatement* checklen = new IfStatement();
- checklen->expression = new Comparison(v, "==", NULL_VALUE);
- checklen->statements->Add(new MethodCall(_data, "writeInt", 1,
- new LiteralExpression("-1")));
- checklen->elseif = new IfStatement();
- checklen->elseif->statements->Add(new MethodCall(_data, "writeInt",
- 1, new FieldVariable(v, "length")));
- tryStatement->statements->Add(checklen);
- }
- else if (dir & IN_PARAMETER) {
- generate_write_to_parcel(t, tryStatement->statements, v, _data, 0);
- }
- arg = arg->next;
- }
-
- // the transact call
- MethodCall* call = new MethodCall(proxyClass->mRemote, "transact", 4,
- new LiteralExpression("Stub." + transactCodeName),
- _data, _reply ? _reply : NULL_VALUE,
- new LiteralExpression(
- oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0"));
- tryStatement->statements->Add(call);
-
- // throw back exceptions.
- if (_reply) {
- MethodCall* ex = new MethodCall(_reply, "readException", 0);
- tryStatement->statements->Add(ex);
- }
-
- // returning and cleanup
- if (_reply != NULL) {
- if (_result != NULL) {
- generate_create_from_parcel(proxy->returnType,
- tryStatement->statements, _result, _reply, &cl);
- }
-
- // the out/inout parameters
- arg = method->args;
- while (arg != NULL) {
- Type* t = NAMES.Search(arg->type.type.data);
- Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
- if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
- generate_read_from_parcel(t, tryStatement->statements,
- v, _reply, &cl);
- }
- arg = arg->next;
- }
-
- finallyStatement->statements->Add(new MethodCall(_reply, "recycle"));
- }
- finallyStatement->statements->Add(new MethodCall(_data, "recycle"));
-
- if (_result != NULL) {
- proxy->statements->Add(new ReturnStatement(_result));
- }
-}
-
-static void
-generate_interface_descriptors(StubClass* stub, ProxyClass* proxy)
-{
- // the interface descriptor transaction handler
- Case* c = new Case("INTERFACE_TRANSACTION");
- c->statements->Add(new MethodCall(stub->transact_reply, "writeString",
- 1, new LiteralExpression("DESCRIPTOR")));
- c->statements->Add(new ReturnStatement(TRUE_VALUE));
- stub->transact_switch->cases.push_back(c);
-
- // and the proxy-side method returning the descriptor directly
- Method* getDesc = new Method;
- getDesc->modifiers = PUBLIC;
- getDesc->returnType = STRING_TYPE;
- getDesc->returnTypeDimension = 0;
- getDesc->name = "getInterfaceDescriptor";
- getDesc->statements = new StatementBlock;
- getDesc->statements->Add(new ReturnStatement(new LiteralExpression("DESCRIPTOR")));
- proxy->elements.push_back(getDesc);
-}
-
-Class*
-generate_binder_interface_class(const interface_type* iface)
-{
- InterfaceType* interfaceType = static_cast<InterfaceType*>(
- NAMES.Find(iface->package, iface->name.data));
-
- // the interface class
- Class* interface = new Class;
- interface->comment = gather_comments(iface->comments_token->extra);
- interface->modifiers = PUBLIC;
- interface->what = Class::INTERFACE;
- interface->type = interfaceType;
- interface->interfaces.push_back(IINTERFACE_TYPE);
-
- // the stub inner class
- StubClass* stub = new StubClass(
- NAMES.Find(iface->package, append(iface->name.data, ".Stub").c_str()),
- interfaceType);
- interface->elements.push_back(stub);
-
- // the proxy inner class
- ProxyClass* proxy = new ProxyClass(
- NAMES.Find(iface->package,
- append(iface->name.data, ".Stub.Proxy").c_str()),
- interfaceType);
- stub->elements.push_back(proxy);
-
- // stub and proxy support for getInterfaceDescriptor()
- generate_interface_descriptors(stub, proxy);
-
- // all the declared methods of the interface
- int index = 0;
- interface_item_type* item = iface->interface_items;
- while (item != NULL) {
- if (item->item_type == METHOD_TYPE) {
- method_type * method_item = (method_type*) item;
- generate_method(method_item, interface, stub, proxy, method_item->assigned_id);
- }
- item = item->next;
- index++;
- }
-
- return interface;
-}
-
diff --git a/tools/aidl/main.cpp b/tools/aidl/main.cpp
deleted file mode 100644
index 7cc2198..0000000
--- a/tools/aidl/main.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "aidl.h"
-#include "options.h"
-
-#include <stdio.h>
-
-int
-main(int argc, const char **argv)
-{
- Options options;
- int result = parse_options(argc, argv, &options);
- if (result) {
- return result;
- }
-
- switch (options.task) {
- case COMPILE_AIDL:
- return compile_aidl(options);
- case PREPROCESS_AIDL:
- return preprocess_aidl(options);
- }
- fprintf(stderr, "aidl: internal error\n");
- return 1;
-}
diff --git a/tools/aidl/options.cpp b/tools/aidl/options.cpp
deleted file mode 100644
index 52b0972..0000000
--- a/tools/aidl/options.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-
-#include "options.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-static int
-usage()
-{
- fprintf(stderr,
- "usage: aidl OPTIONS INPUT [OUTPUT]\n"
- " aidl --preprocess OUTPUT INPUT...\n"
- "\n"
- "OPTIONS:\n"
- " -I<DIR> search path for import statements.\n"
- " -d<FILE> generate dependency file.\n"
- " -a generate dependency file next to the output file with the name based on the input file.\n"
- " -p<FILE> file created by --preprocess to import.\n"
- " -o<FOLDER> base output folder for generated files.\n"
- " -b fail when trying to compile a parcelable.\n"
- "\n"
- "INPUT:\n"
- " An aidl interface file.\n"
- "\n"
- "OUTPUT:\n"
- " The generated interface files.\n"
- " If omitted and the -o option is not used, the input filename is used, with the .aidl extension changed to a .java extension.\n"
- " If the -o option is used, the generated files will be placed in the base output folder, under their package folder\n"
- );
- return 1;
-}
-
-int
-parse_options(int argc, const char* const* argv, Options *options)
-{
- int i = 1;
-
- if (argc >= 2 && 0 == strcmp(argv[1], "--preprocess")) {
- if (argc < 4) {
- return usage();
- }
- options->outputFileName = argv[2];
- for (int i=3; i<argc; i++) {
- options->filesToPreprocess.push_back(argv[i]);
- }
- options->task = PREPROCESS_AIDL;
- return 0;
- }
-
- // OPTIONS
- while (i < argc) {
- const char* s = argv[i];
- int len = strlen(s);
- if (s[0] == '-') {
- if (len > 1) {
- // -I<system-import-path>
- if (s[1] == 'I') {
- if (len > 2) {
- options->importPaths.push_back(s+2);
- } else {
- fprintf(stderr, "-I option (%d) requires a path.\n", i);
- return usage();
- }
- }
- else if (s[1] == 'd') {
- if (len > 2) {
- options->depFileName = s+2;
- } else {
- fprintf(stderr, "-d option (%d) requires a file.\n", i);
- return usage();
- }
- }
- else if (s[1] == 'a') {
- options->autoDepFile = true;
- }
- else if (s[1] == 'p') {
- if (len > 2) {
- options->preprocessedFiles.push_back(s+2);
- } else {
- fprintf(stderr, "-p option (%d) requires a file.\n", i);
- return usage();
- }
- }
- else if (s[1] == 'o') {
- if (len > 2) {
- options->outputBaseFolder = s+2;
- } else {
- fprintf(stderr, "-o option (%d) requires a path.\n", i);
- return usage();
- }
- }
- else if (len == 2 && s[1] == 'b') {
- options->failOnParcelable = true;
- }
- else {
- // s[1] is not known
- fprintf(stderr, "unknown option (%d): %s\n", i, s);
- return usage();
- }
- } else {
- // len <= 1
- fprintf(stderr, "unknown option (%d): %s\n", i, s);
- return usage();
- }
- } else {
- // s[0] != '-'
- break;
- }
- i++;
- }
-
- // INPUT
- if (i < argc) {
- options->inputFileName = argv[i];
- i++;
- } else {
- fprintf(stderr, "INPUT required\n");
- return usage();
- }
-
- // OUTPUT
- if (i < argc) {
- options->outputFileName = argv[i];
- i++;
- } else if (options->outputBaseFolder.length() == 0) {
- // copy input into output and change the extension from .aidl to .java
- options->outputFileName = options->inputFileName;
- string::size_type pos = options->outputFileName.size()-5;
- if (options->outputFileName.compare(pos, 5, ".aidl") == 0) { // 5 = strlen(".aidl")
- options->outputFileName.replace(pos, 5, ".java"); // 5 = strlen(".aidl")
- } else {
- fprintf(stderr, "INPUT is not an .aidl file.\n");
- return usage();
- }
- }
-
- // anything remaining?
- if (i != argc) {
- fprintf(stderr, "unknown option%s:", (i==argc-1?(const char*)"":(const char*)"s"));
- for (; i<argc-1; i++) {
- fprintf(stderr, " %s", argv[i]);
- }
- fprintf(stderr, "\n");
- return usage();
- }
-
- return 0;
-}
-
diff --git a/tools/aidl/options.h b/tools/aidl/options.h
deleted file mode 100644
index 4e95e11..0000000
--- a/tools/aidl/options.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef AIDL_OPTIONS_H_
-#define AIDL_OPTIONS_H_
-
-#include <string.h>
-#include <string>
-#include <vector>
-
-using namespace std;
-
-enum {
- COMPILE_AIDL,
- PREPROCESS_AIDL
-};
-
-// This struct is the parsed version of the command line options
-struct Options
-{
- int task{COMPILE_AIDL};
- bool failOnParcelable{false};
- vector<string> importPaths;
- vector<string> preprocessedFiles;
- string inputFileName;
- string outputFileName;
- string outputBaseFolder;
- string depFileName;
- bool autoDepFile{false};
-
- vector<string> filesToPreprocess;
-};
-
-// takes the inputs from the command line and fills in the Options struct
-// Returns 0 on success, and nonzero on failure.
-// It also prints the usage statement on failure.
-int parse_options(int argc, const char* const* argv, Options *options);
-
-#endif // AIDL_OPTIONS_H_
diff --git a/tools/aidl/options_unittest.cpp b/tools/aidl/options_unittest.cpp
deleted file mode 100644
index fec7f87..0000000
--- a/tools/aidl/options_unittest.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include "options.h"
-
-using std::vector;
-using std::string;
-
-const char kPreprocessCommandOutputFile[] = "output_file_name";
-const char kPreprocessCommandInput1[] = "input1";
-const char kPreprocessCommandInput2[] = "input2";
-const char kPreprocessCommandInput3[] = "input3";
-const char* kPreprocessCommand[] = {
- "aidl", "--preprocess",
- kPreprocessCommandOutputFile,
- kPreprocessCommandInput1,
- kPreprocessCommandInput2,
- kPreprocessCommandInput3,
-};
-
-TEST(OptionsTests, ParsesPreprocess) {
- Options options;
- const int argc = sizeof(kPreprocessCommand) / sizeof(*kPreprocessCommand);
- EXPECT_EQ(parse_options(argc, kPreprocessCommand, &options), 0);
- EXPECT_EQ(options.task, PREPROCESS_AIDL);
- EXPECT_EQ(options.failOnParcelable, false);
- EXPECT_EQ(options.importPaths.size(), 0u);
- EXPECT_EQ(options.preprocessedFiles.size(), 0u);
- EXPECT_EQ(options.inputFileName, string{""});
- EXPECT_EQ(options.outputFileName, string{kPreprocessCommandOutputFile});
- EXPECT_EQ(options.autoDepFile, false);
- const vector<string> expected_input{kPreprocessCommandInput1,
- kPreprocessCommandInput2,
- kPreprocessCommandInput3};
- EXPECT_EQ(options.filesToPreprocess, expected_input);
-}
diff --git a/tools/aidl/search_path.cpp b/tools/aidl/search_path.cpp
deleted file mode 100644
index 029e216..0000000
--- a/tools/aidl/search_path.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <unistd.h>
-#include "search_path.h"
-#include "options.h"
-#include "os.h"
-#include <string.h>
-
-#ifdef _WIN32
-#include <io.h>
-#endif
-
-static vector<string> g_importPaths;
-
-void
-set_import_paths(const vector<string>& importPaths)
-{
- g_importPaths = importPaths;
-}
-
-char*
-find_import_file(const char* given)
-{
- string expected = given;
-
- int N = expected.length();
- for (int i=0; i<N; i++) {
- char c = expected[i];
- if (c == '.') {
- expected[i] = OS_PATH_SEPARATOR;
- }
- }
- expected += ".aidl";
-
- vector<string>& paths = g_importPaths;
- for (vector<string>::iterator it=paths.begin(); it!=paths.end(); it++) {
- string f = *it;
- if (f.size() == 0) {
- f = ".";
- f += OS_PATH_SEPARATOR;
- }
- else if (f[f.size()-1] != OS_PATH_SEPARATOR) {
- f += OS_PATH_SEPARATOR;
- }
- f.append(expected);
-
-#ifdef _WIN32
- /* check that the file exists and is not write-only */
- if (0 == _access(f.c_str(), 0) && /* mode 0=exist */
- 0 == _access(f.c_str(), 4) ) { /* mode 4=readable */
-#else
- if (0 == access(f.c_str(), R_OK)) {
-#endif
- return strdup(f.c_str());
- }
- }
-
- return NULL;
-}
-
diff --git a/tools/aidl/search_path.h b/tools/aidl/search_path.h
deleted file mode 100644
index 4fec257..0000000
--- a/tools/aidl/search_path.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef AIDL_SEARCH_PATH_H_
-#define AIDL_SEARCH_PATH_H_
-
-#include <stdio.h>
-
-#if __cplusplus
-#include <vector>
-#include <string>
-using namespace std;
-extern "C" {
-#endif
-
-// returns a FILE* and the char* for the file that it found
-// given is the class name we're looking for
-char* find_import_file(const char* given);
-
-#if __cplusplus
-}; // extern "C"
-void set_import_paths(const vector<string>& importPaths);
-#endif
-
-#endif // AIDL_SEARCH_PATH_H_
diff --git a/tools/aidl/test_main.cpp b/tools/aidl/test_main.cpp
deleted file mode 100644
index 4d820af7..0000000
--- a/tools/aidl/test_main.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include <gtest/gtest.h>
-
-int main(int argc, char **argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/tools/aidl/tests/end_to_end_tests.cpp b/tools/aidl/tests/end_to_end_tests.cpp
deleted file mode 100644
index 54ca603..0000000
--- a/tools/aidl/tests/end_to_end_tests.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <base/logging.h>
-#include <base/files/file_path.h>
-#include <base/files/file_util.h>
-#include <gtest/gtest.h>
-
-#include "aidl.h"
-#include "options.h"
-#include "tests/test_data.h"
-
-using base::FilePath;
-using std::string;
-using std::unique_ptr;
-using std::vector;
-
-using namespace aidl::test_data;
-
-namespace {
-
-const char kDiffTemplate[] = "diff %s %s";
-const char kStubInterfaceTemplate[] = "package %s;\ninterface %s { }";
-const char kStubParcelableTemplate[] = "package %s;\nparcelable %s;";
-
-FilePath GetPathForPackageClass(const char* package_class,
- const char* extension) {
- string rel_path{package_class};
- for (char& c : rel_path) {
- if (c == '.') {
- c = FilePath::kSeparators[0];
- }
- }
- rel_path += extension;
- return FilePath(rel_path);
-}
-
-void SplitPackageClass(const string& package_class,
- FilePath* rel_path,
- string* package,
- string* class_name) {
- *package = string{package_class, 0, package_class.rfind('.')};
- *class_name = string{package_class, package_class.rfind('.') + 1};
- *rel_path = GetPathForPackageClass(package_class.c_str(), ".aidl");
-}
-
-} // namespace
-
-class EndToEndTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- ASSERT_TRUE(base::CreateNewTempDirectory(
- string{"end_to_end_testsyyyy"}, &tmpDir_));
- inputDir_ = tmpDir_.Append("input");
- outputDir_ = tmpDir_.Append("output");
- ASSERT_TRUE(base::CreateDirectory(inputDir_));
- ASSERT_TRUE(base::CreateDirectory(outputDir_));
- }
-
- virtual void TearDown() {
- ASSERT_TRUE(DeleteFile(tmpDir_, true))
- << "Failed to remove temp directory: " << tmpDir_.value();
- }
-
- FilePath CreateInputFile(const FilePath& relative_path,
- const char contents[],
- int size) {
- const FilePath created_file = inputDir_.Append(relative_path);
- EXPECT_TRUE(base::CreateDirectory(created_file.DirName()));
- EXPECT_TRUE(base::WriteFile(created_file, contents, size));
- return created_file;
- }
-
- void CreateStubAidlFile(const string& package_class,
- const char* file_template) {
- string package, class_name;
- FilePath rel_path;
- SplitPackageClass(package_class, &rel_path, &package, &class_name);
- const size_t buf_len =
- strlen(file_template) + package.length() + class_name.length() + 1;
- unique_ptr<char[]> contents(new char[buf_len]);
- const int written = snprintf(contents.get(), buf_len, file_template,
- package.c_str(), class_name.c_str());
- EXPECT_GT(written, 0);
- CreateInputFile(rel_path, contents.get(), written);
- }
-
- void WriteStubAidls(const char** parcelables, const char** interfaces) {
- while (*parcelables) {
- CreateStubAidlFile(string{*parcelables}, kStubParcelableTemplate);
- ++parcelables;
- }
- while (*interfaces) {
- CreateStubAidlFile(string{*interfaces}, kStubInterfaceTemplate);
- ++interfaces;
- }
- }
-
- void CheckFileContents(const FilePath& rel_path,
- const string& expected_content) {
- string actual_contents;
- FilePath actual_path = outputDir_.Append(rel_path);
- if (!ReadFileToString(actual_path, &actual_contents)) {
- FAIL() << "Failed to read expected output file: " << rel_path.value();
- }
- // Generated .java files mention the "original" file as part of their
- // comment header. Thus we look for expected_content being a substring.
- if (actual_contents.find(expected_content) == string::npos) {
- // When the match fails, display a diff of what's wrong. This greatly
- // aids in debugging.
- FilePath expected_path;
- EXPECT_TRUE(CreateTemporaryFileInDir(tmpDir_, &expected_path));
- base::WriteFile(expected_path, expected_content.c_str(),
- expected_content.length());
- const size_t buf_len =
- strlen(kDiffTemplate) + actual_path.value().length() +
- expected_path.value().length() + 1;
- unique_ptr<char[]> diff_cmd(new char[buf_len]);
- EXPECT_GT(snprintf(diff_cmd.get(), buf_len, kDiffTemplate,
- expected_path.value().c_str(),
- actual_path.value().c_str()), 0);
- system(diff_cmd.get());
- FAIL() << "Actual contents of " << rel_path.value()
- << " did not match expected content";
- }
- }
-
- FilePath tmpDir_;
- FilePath inputDir_;
- FilePath outputDir_;
-};
-
-TEST_F(EndToEndTest, IExampleInterface) {
- Options options;
- options.failOnParcelable = true;
- options.importPaths.push_back(inputDir_.value());
- options.inputFileName =
- CreateInputFile(GetPathForPackageClass(kIExampleInterfaceClass, ".aidl"),
- kIExampleInterfaceContents,
- strlen(kIExampleInterfaceContents)).value();
- options.autoDepFile = true;
- options.outputBaseFolder = outputDir_.value();
- WriteStubAidls(kIExampleInterfaceParcelables, kIExampleInterfaceInterfaces);
- EXPECT_EQ(compile_aidl(options), 0);
- CheckFileContents(GetPathForPackageClass(kIExampleInterfaceClass, ".java"),
- kIExampleInterfaceJava);
- // We'd like to check the depends file, but it mentions unique file paths.
-}
diff --git a/tools/aidl/tests/example_interface_test_data.cpp b/tools/aidl/tests/example_interface_test_data.cpp
deleted file mode 100644
index b17a800..0000000
--- a/tools/aidl/tests/example_interface_test_data.cpp
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "tests/test_data.h"
-
-namespace aidl {
-namespace test_data {
-
-const char kIExampleInterfaceClass[] = "android.test.IExampleInterface";
-
-const char* kIExampleInterfaceParcelables[] = {
- "android.foo.ExampleParcelable",
- "android.test.ExampleParcelable2",
- nullptr,
-};
-
-const char* kIExampleInterfaceInterfaces[] = {
- "android.bar.IAuxInterface",
- "android.test.IAuxInterface2",
- nullptr,
-};
-
-const char kIExampleInterfaceContents[] = R"(
-package android.test;
-
-import android.foo.ExampleParcelable;
-import android.test.ExampleParcelable2;
-import android.bar.IAuxInterface;
-import android.test.IAuxInterface2;
-
-interface IExampleInterface {
- boolean isEnabled();
- int getState();
- String getAddress();
-
- ExampleParcelable[] getParcelables();
-
- boolean setScanMode(int mode, int duration);
-
- void registerBinder(IAuxInterface foo);
- IExampleInterface getRecursiveBinder();
-
- int takesAnInterface(in IAuxInterface2 arg);
- int takesAParcelable(in ExampleParcelable2 arg);
-}
-)";
-
-const char kIExampleInterfaceDeps[] =
-R"(/tmp/.org.chromium.Chromium.Cdq7YZ/output/android/test/IExampleInterface.java: \
- /tmp/.org.chromium.Chromium.Cdq7YZ/input/android/test/IExampleInterface.aidl \
- /tmp/.org.chromium.Chromium.Cdq7YZ/input/android/foo/ExampleParcelable.aidl \
- /tmp/.org.chromium.Chromium.Cdq7YZ/input/android/test/ExampleParcelable2.aidl \
- /tmp/.org.chromium.Chromium.Cdq7YZ/input/android/bar/IAuxInterface.aidl \
- /tmp/.org.chromium.Chromium.Cdq7YZ/input/android/test/IAuxInterface2.aidl
-
-/tmp/.org.chromium.Chromium.Cdq7YZ/input/android/test/IExampleInterface.aidl :
-/tmp/.org.chromium.Chromium.Cdq7YZ/input/android/foo/ExampleParcelable.aidl :
-/tmp/.org.chromium.Chromium.Cdq7YZ/input/android/test/ExampleParcelable2.aidl :
-/tmp/.org.chromium.Chromium.Cdq7YZ/input/android/bar/IAuxInterface.aidl :
-/tmp/.org.chromium.Chromium.Cdq7YZ/input/android/test/IAuxInterface2.aidl :)";
-
-const char kIExampleInterfaceJava[] =
-R"(package android.test;
-public interface IExampleInterface extends android.os.IInterface
-{
-/** Local-side IPC implementation stub class. */
-public static abstract class Stub extends android.os.Binder implements android.test.IExampleInterface
-{
-private static final java.lang.String DESCRIPTOR = "android.test.IExampleInterface";
-/** Construct the stub at attach it to the interface. */
-public Stub()
-{
-this.attachInterface(this, DESCRIPTOR);
-}
-/**
- * Cast an IBinder object into an android.test.IExampleInterface interface,
- * generating a proxy if needed.
- */
-public static android.test.IExampleInterface asInterface(android.os.IBinder obj)
-{
-if ((obj==null)) {
-return null;
-}
-android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
-if (((iin!=null)&&(iin instanceof android.test.IExampleInterface))) {
-return ((android.test.IExampleInterface)iin);
-}
-return new android.test.IExampleInterface.Stub.Proxy(obj);
-}
-@Override public android.os.IBinder asBinder()
-{
-return this;
-}
-@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
-{
-switch (code)
-{
-case INTERFACE_TRANSACTION:
-{
-reply.writeString(DESCRIPTOR);
-return true;
-}
-case TRANSACTION_isEnabled:
-{
-data.enforceInterface(DESCRIPTOR);
-boolean _result = this.isEnabled();
-reply.writeNoException();
-reply.writeInt(((_result)?(1):(0)));
-return true;
-}
-case TRANSACTION_getState:
-{
-data.enforceInterface(DESCRIPTOR);
-int _result = this.getState();
-reply.writeNoException();
-reply.writeInt(_result);
-return true;
-}
-case TRANSACTION_getAddress:
-{
-data.enforceInterface(DESCRIPTOR);
-java.lang.String _result = this.getAddress();
-reply.writeNoException();
-reply.writeString(_result);
-return true;
-}
-case TRANSACTION_getParcelables:
-{
-data.enforceInterface(DESCRIPTOR);
-android.foo.ExampleParcelable[] _result = this.getParcelables();
-reply.writeNoException();
-reply.writeTypedArray(_result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
-return true;
-}
-case TRANSACTION_setScanMode:
-{
-data.enforceInterface(DESCRIPTOR);
-int _arg0;
-_arg0 = data.readInt();
-int _arg1;
-_arg1 = data.readInt();
-boolean _result = this.setScanMode(_arg0, _arg1);
-reply.writeNoException();
-reply.writeInt(((_result)?(1):(0)));
-return true;
-}
-case TRANSACTION_registerBinder:
-{
-data.enforceInterface(DESCRIPTOR);
-android.bar.IAuxInterface _arg0;
-_arg0 = android.bar.IAuxInterface.Stub.asInterface(data.readStrongBinder());
-this.registerBinder(_arg0);
-reply.writeNoException();
-return true;
-}
-case TRANSACTION_getRecursiveBinder:
-{
-data.enforceInterface(DESCRIPTOR);
-android.test.IExampleInterface _result = this.getRecursiveBinder();
-reply.writeNoException();
-reply.writeStrongBinder((((_result!=null))?(_result.asBinder()):(null)));
-return true;
-}
-case TRANSACTION_takesAnInterface:
-{
-data.enforceInterface(DESCRIPTOR);
-android.test.IAuxInterface2 _arg0;
-_arg0 = android.test.IAuxInterface2.Stub.asInterface(data.readStrongBinder());
-int _result = this.takesAnInterface(_arg0);
-reply.writeNoException();
-reply.writeInt(_result);
-return true;
-}
-case TRANSACTION_takesAParcelable:
-{
-data.enforceInterface(DESCRIPTOR);
-android.test.ExampleParcelable2 _arg0;
-if ((0!=data.readInt())) {
-_arg0 = android.test.ExampleParcelable2.CREATOR.createFromParcel(data);
-}
-else {
-_arg0 = null;
-}
-int _result = this.takesAParcelable(_arg0);
-reply.writeNoException();
-reply.writeInt(_result);
-return true;
-}
-}
-return super.onTransact(code, data, reply, flags);
-}
-private static class Proxy implements android.test.IExampleInterface
-{
-private android.os.IBinder mRemote;
-Proxy(android.os.IBinder remote)
-{
-mRemote = remote;
-}
-@Override public android.os.IBinder asBinder()
-{
-return mRemote;
-}
-public java.lang.String getInterfaceDescriptor()
-{
-return DESCRIPTOR;
-}
-@Override public boolean isEnabled() throws android.os.RemoteException
-{
-android.os.Parcel _data = android.os.Parcel.obtain();
-android.os.Parcel _reply = android.os.Parcel.obtain();
-boolean _result;
-try {
-_data.writeInterfaceToken(DESCRIPTOR);
-mRemote.transact(Stub.TRANSACTION_isEnabled, _data, _reply, 0);
-_reply.readException();
-_result = (0!=_reply.readInt());
-}
-finally {
-_reply.recycle();
-_data.recycle();
-}
-return _result;
-}
-@Override public int getState() throws android.os.RemoteException
-{
-android.os.Parcel _data = android.os.Parcel.obtain();
-android.os.Parcel _reply = android.os.Parcel.obtain();
-int _result;
-try {
-_data.writeInterfaceToken(DESCRIPTOR);
-mRemote.transact(Stub.TRANSACTION_getState, _data, _reply, 0);
-_reply.readException();
-_result = _reply.readInt();
-}
-finally {
-_reply.recycle();
-_data.recycle();
-}
-return _result;
-}
-@Override public java.lang.String getAddress() throws android.os.RemoteException
-{
-android.os.Parcel _data = android.os.Parcel.obtain();
-android.os.Parcel _reply = android.os.Parcel.obtain();
-java.lang.String _result;
-try {
-_data.writeInterfaceToken(DESCRIPTOR);
-mRemote.transact(Stub.TRANSACTION_getAddress, _data, _reply, 0);
-_reply.readException();
-_result = _reply.readString();
-}
-finally {
-_reply.recycle();
-_data.recycle();
-}
-return _result;
-}
-@Override public android.foo.ExampleParcelable[] getParcelables() throws android.os.RemoteException
-{
-android.os.Parcel _data = android.os.Parcel.obtain();
-android.os.Parcel _reply = android.os.Parcel.obtain();
-android.foo.ExampleParcelable[] _result;
-try {
-_data.writeInterfaceToken(DESCRIPTOR);
-mRemote.transact(Stub.TRANSACTION_getParcelables, _data, _reply, 0);
-_reply.readException();
-_result = _reply.createTypedArray(android.foo.ExampleParcelable.CREATOR);
-}
-finally {
-_reply.recycle();
-_data.recycle();
-}
-return _result;
-}
-@Override public boolean setScanMode(int mode, int duration) throws android.os.RemoteException
-{
-android.os.Parcel _data = android.os.Parcel.obtain();
-android.os.Parcel _reply = android.os.Parcel.obtain();
-boolean _result;
-try {
-_data.writeInterfaceToken(DESCRIPTOR);
-_data.writeInt(mode);
-_data.writeInt(duration);
-mRemote.transact(Stub.TRANSACTION_setScanMode, _data, _reply, 0);
-_reply.readException();
-_result = (0!=_reply.readInt());
-}
-finally {
-_reply.recycle();
-_data.recycle();
-}
-return _result;
-}
-@Override public void registerBinder(android.bar.IAuxInterface foo) throws android.os.RemoteException
-{
-android.os.Parcel _data = android.os.Parcel.obtain();
-android.os.Parcel _reply = android.os.Parcel.obtain();
-try {
-_data.writeInterfaceToken(DESCRIPTOR);
-_data.writeStrongBinder((((foo!=null))?(foo.asBinder()):(null)));
-mRemote.transact(Stub.TRANSACTION_registerBinder, _data, _reply, 0);
-_reply.readException();
-}
-finally {
-_reply.recycle();
-_data.recycle();
-}
-}
-@Override public android.test.IExampleInterface getRecursiveBinder() throws android.os.RemoteException
-{
-android.os.Parcel _data = android.os.Parcel.obtain();
-android.os.Parcel _reply = android.os.Parcel.obtain();
-android.test.IExampleInterface _result;
-try {
-_data.writeInterfaceToken(DESCRIPTOR);
-mRemote.transact(Stub.TRANSACTION_getRecursiveBinder, _data, _reply, 0);
-_reply.readException();
-_result = android.test.IExampleInterface.Stub.asInterface(_reply.readStrongBinder());
-}
-finally {
-_reply.recycle();
-_data.recycle();
-}
-return _result;
-}
-@Override public int takesAnInterface(android.test.IAuxInterface2 arg) throws android.os.RemoteException
-{
-android.os.Parcel _data = android.os.Parcel.obtain();
-android.os.Parcel _reply = android.os.Parcel.obtain();
-int _result;
-try {
-_data.writeInterfaceToken(DESCRIPTOR);
-_data.writeStrongBinder((((arg!=null))?(arg.asBinder()):(null)));
-mRemote.transact(Stub.TRANSACTION_takesAnInterface, _data, _reply, 0);
-_reply.readException();
-_result = _reply.readInt();
-}
-finally {
-_reply.recycle();
-_data.recycle();
-}
-return _result;
-}
-@Override public int takesAParcelable(android.test.ExampleParcelable2 arg) throws android.os.RemoteException
-{
-android.os.Parcel _data = android.os.Parcel.obtain();
-android.os.Parcel _reply = android.os.Parcel.obtain();
-int _result;
-try {
-_data.writeInterfaceToken(DESCRIPTOR);
-if ((arg!=null)) {
-_data.writeInt(1);
-arg.writeToParcel(_data, 0);
-}
-else {
-_data.writeInt(0);
-}
-mRemote.transact(Stub.TRANSACTION_takesAParcelable, _data, _reply, 0);
-_reply.readException();
-_result = _reply.readInt();
-}
-finally {
-_reply.recycle();
-_data.recycle();
-}
-return _result;
-}
-}
-static final int TRANSACTION_isEnabled = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
-static final int TRANSACTION_getState = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
-static final int TRANSACTION_getAddress = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
-static final int TRANSACTION_getParcelables = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
-static final int TRANSACTION_setScanMode = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
-static final int TRANSACTION_registerBinder = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
-static final int TRANSACTION_getRecursiveBinder = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
-static final int TRANSACTION_takesAnInterface = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
-static final int TRANSACTION_takesAParcelable = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
-}
-public boolean isEnabled() throws android.os.RemoteException;
-public int getState() throws android.os.RemoteException;
-public java.lang.String getAddress() throws android.os.RemoteException;
-public android.foo.ExampleParcelable[] getParcelables() throws android.os.RemoteException;
-public boolean setScanMode(int mode, int duration) throws android.os.RemoteException;
-public void registerBinder(android.bar.IAuxInterface foo) throws android.os.RemoteException;
-public android.test.IExampleInterface getRecursiveBinder() throws android.os.RemoteException;
-public int takesAnInterface(android.test.IAuxInterface2 arg) throws android.os.RemoteException;
-public int takesAParcelable(android.test.ExampleParcelable2 arg) throws android.os.RemoteException;
-})";
-
-} // namespace test_data
-} // namespace aidl
diff --git a/tools/aidl/tests/test_data.h b/tools/aidl/tests/test_data.h
deleted file mode 100644
index cd8887f..0000000
--- a/tools/aidl/tests/test_data.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AIDL_TESTS_TEST_DATA_H_
-#define AIDL_TESTS_TEST_DATA_H_
-
-namespace aidl {
-namespace test_data {
-
-extern const char kIExampleInterfaceClass[];
-extern const char kIExampleInterfaceContents[];
-extern const char* kIExampleInterfaceParcelables[];
-extern const char* kIExampleInterfaceInterfaces[];
-
-extern const char kIExampleInterfaceDeps[];
-extern const char kIExampleInterfaceJava[];
-
-} // namespace test_data
-} // namespace aidl
-#endif // AIDL_TESTS_TEST_DATA_H_
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index 163fbcb..3b7bf85 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
@@ -48,9 +48,6 @@
import java.io.InputStream;
import java.util.Iterator;
-/**
- *
- */
public final class BridgeResources extends Resources {
private BridgeContext mContext;
@@ -278,7 +275,7 @@
* always Strings. The ideal signature for the method should be <T super String>, but java
* generics don't support it.
*/
- private <T extends CharSequence> T[] fillValues(ArrayResourceValue resValue, T[] values) {
+ <T extends CharSequence> T[] fillValues(ArrayResourceValue resValue, T[] values) {
int i = 0;
for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
@SuppressWarnings("unchecked")
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 6a61090..31dd3d9 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -16,6 +16,7 @@
package android.content.res;
+import com.android.ide.common.rendering.api.ArrayResourceValue;
import com.android.ide.common.rendering.api.AttrResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderResources;
@@ -33,6 +34,7 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.Nullable;
+import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
@@ -740,12 +742,20 @@
*/
@Override
public CharSequence[] getTextArray(int index) {
- String value = getString(index);
- if (value != null) {
- return new CharSequence[] { value };
+ if (!hasValue(index)) {
+ return null;
}
-
- return null;
+ ResourceValue resVal = mResourceData[index];
+ if (resVal instanceof ArrayResourceValue) {
+ ArrayResourceValue array = (ArrayResourceValue) resVal;
+ int count = array.getElementCount();
+ return count >= 0 ? mBridgeResources.fillValues(array, new CharSequence[count]) : null;
+ }
+ int id = getResourceId(index, 0);
+ String resIdMessage = id > 0 ? " (resource id 0x" + Integer.toHexString(id) + ')' : "";
+ throw new NotFoundException(
+ String.format("%1$s in %2$s%3$s is not a valid array resource.",
+ resVal.getValue(), mNames[index], resIdMessage));
}
@Override
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index ed8b56e..0da6bb6 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -76,7 +76,7 @@
@Override
public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10,
- Rect arg11, Configuration arg12) throws RemoteException {
+ Rect arg11, Configuration arg12, boolean arg13) throws RemoteException {
// TODO Auto-generated method stub
}
@@ -338,11 +338,6 @@
}
@Override
- public void setAppWillBeHidden(IBinder arg0) throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
public void setEventDispatching(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index f04654e..e3a19e7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -65,6 +65,12 @@
}
@Override
+ public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+ throws NameNotFoundException {
+ return null;
+ }
+
+ @Override
public String[] currentToCanonicalPackageNames(String[] names) {
return new String[0];
}
@@ -499,6 +505,11 @@
}
@Override
+ public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer,int flags,
+ String installerPackageName, int userId) {
+ }
+
+ @Override
public void installPackageWithVerification(Uri packageURI, PackageInstallObserver observer,
int flags, String installerPackageName, Uri verificationURI,
ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
@@ -516,6 +527,12 @@
}
@Override
+ public int installExistingPackageAsUser(String packageName, int userId)
+ throws NameNotFoundException {
+ return 0;
+ }
+
+ @Override
public void verifyPendingInstall(int id, int verificationCode) {
}
@@ -568,6 +585,11 @@
}
@Override
+ public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
+ int userId) {
+ }
+
+ @Override
public String getInstallerPackageName(String packageName) {
return null;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index bea1f86..2997907 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -148,6 +148,13 @@
}
@Override
+ public boolean startMovingTask(IWindow window, float startX, float startY)
+ throws RemoteException {
+ // pass for now
+ return false;
+ }
+
+ @Override
public void reportDropResult(IWindow window, boolean consumed) throws RemoteException {
// pass for now
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 24e1ce7..2a4f583 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -1050,11 +1050,7 @@
}
if (scrollPos != 0) {
view.scrollBy(0, scrollPos);
- } else {
- view.scrollBy(0, scrollPos);
}
- } else {
- view.scrollBy(0, scrollPos);
}
if (!(view instanceof ViewGroup)) {
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0d95b38..23be8e0 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -23,6 +23,7 @@
import android.net.wifi.ScanSettings;
import android.net.wifi.WifiChannel;
import android.net.wifi.ScanResult;
+import android.net.wifi.ScanInfo;
import android.net.wifi.WifiConnectionStatistics;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.Network;
@@ -70,6 +71,10 @@
void disconnect();
+ List<ScanInfo> getScanInfos(String callingPackage);
+
+ void setOsuSelection(int osuID);
+
void reconnect();
void reassociate();
diff --git a/tools/aidl/os.h b/wifi/java/android/net/wifi/ScanInfo.aidl
similarity index 72%
rename from tools/aidl/os.h
rename to wifi/java/android/net/wifi/ScanInfo.aidl
index 752ed47..18ae508 100644
--- a/tools/aidl/os.h
+++ b/wifi/java/android/net/wifi/ScanInfo.aidl
@@ -1,5 +1,5 @@
-/*
- * Copyright 2015, The Android Open Source Project
+/**
+ * Copyright (c) 2015, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,6 @@
* limitations under the License.
*/
-#ifndef AIDL_OS_H_
-#define AIDL_OS_H_
+package android.net.wifi;
-#if defined(_WIN32)
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
-
-#endif // AIDL_OS_H_
+parcelable ScanInfo;
diff --git a/wifi/java/android/net/wifi/ScanInfo.java b/wifi/java/android/net/wifi/ScanInfo.java
new file mode 100644
index 0000000..39186fa
--- /dev/null
+++ b/wifi/java/android/net/wifi/ScanInfo.java
@@ -0,0 +1,189 @@
+package android.net.wifi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ScanInfo implements Parcelable {
+ private final ScanResult mScanResult;
+
+ private final long mBSSID; // The BSSID of the best AP with an SSID matching the OSU
+ private final int mRSSI; // RSSI of the AP with BSSID
+ private final String mSSID; // The SSID to connect to for an OSU connection.
+ private final String mName;
+ private final String mServiceDescription;
+ private final String mIconType;
+ private final byte[] mIconData;
+ private final int mOSUIdentity;
+
+ public ScanInfo(ScanResult scanResult) {
+ mScanResult = scanResult;
+
+ mBSSID = -1;
+ mRSSI = -1;
+ mSSID = null;
+ mName = null;
+ mServiceDescription = null;
+ mIconType = null;
+ mIconData = null;
+ mOSUIdentity = -1;
+ }
+
+ public ScanInfo(long BSSID, int rssi, String SSID, String name, String serviceDescription,
+ String iconType, byte[] iconData, int OSUIdentity) {
+ mBSSID = BSSID;
+ mRSSI = rssi;
+ mSSID = SSID;
+ mName = name;
+ mServiceDescription = serviceDescription;
+ mIconType = iconType;
+ mIconData = iconData;
+ mOSUIdentity = OSUIdentity;
+
+ mScanResult = null;
+ }
+
+ /**
+ * Get the scan result of this ScanInfo.
+ * @return The ScanResult, if this ScanInfo contains a one. If the ScanInfo contains
+ * OSU information getScanResult will return null.
+ */
+ public ScanResult getScanResult() {
+ return mScanResult;
+ }
+
+ /**
+ * OSU only: The BSSID of the AP who advertises the OSU SSID. This value is not guaranteed to
+ * be correct; In the somewhat unlikely case that multiple APs advertise OSU SSIDs that matches
+ * an OSU information element returned through ANQP and one of those is not related to an OSU
+ * there is a (slight) risk that the BSSID is for a "spoof" OSU.
+ * The matching algorithm that produces the ScanInfo objects makes a best effort to get the
+ * matching right though and since it is (a) fair to assume that the OSU SSID resides on the
+ * same AP as the one advertising the OSU information, and (b) BSSIDs for multi-SSID APs are
+ * typically adjacent to each other, matching will prefer the BSSID closest to the advertising
+ * APs BSSID if multiple SSIDs match.
+ * @return The BSSID.
+ */
+ public long getBssid() {
+ return mBSSID;
+ }
+
+ /**
+ * OSU only.
+ * @return The signal level of the AP associated with the BSSID from getBSSID.
+ */
+ public int getRssi() {
+ return mRSSI;
+ }
+
+ /**
+ * OSU only.
+ * @return The SSID of the AP to which to associate to establish an OSU connection.
+ */
+ public String getSsid() {
+ return mSSID;
+ }
+
+ /**
+ * OSU only.
+ * @return The name of the Service Provider of the OSU.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * OSU only.
+ * @return The service description of the OSU.
+ */
+ public String getServiceDescription() {
+ return mServiceDescription;
+ }
+
+ /**
+ * OSU only.
+ * Get the type of icon that icon data represents, e.g. JPG, PNG etc. This field is formatted
+ * using standard MIME encodings per RFC-4288 and IANA MIME media types.
+ * @return The icon type in icon data.
+ */
+ public String getIconType() {
+ return mIconType;
+ }
+
+ /**
+ * OSU only.
+ * @return The binary data of the icon.
+ */
+ public byte[] getIconData() {
+ return mIconData;
+ }
+
+ /**
+ * OSU only.
+ * @return a unique identity for the OSU. This value is generated by the framework and should
+ * be used to uniquely identify a specific OSU. Please note that values may be reused after
+ * a very long time-span (in any normal scenario, likely years) and implementations should make
+ * sure to not rely on any long term persisted values.
+ */
+ public int getOsuIdentity() {
+ return mOSUIdentity;
+ }
+
+ private static final int ScanResultMarker = 0;
+ private static final int OSUMarker = 1;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<ScanInfo> CREATOR =
+ new Creator<ScanInfo>() {
+ @Override
+ public ScanInfo createFromParcel(Parcel source) {
+ int marker = source.readInt();
+ if (marker == ScanResultMarker) {
+ return new ScanInfo(ScanResult.CREATOR.createFromParcel(source));
+ }
+ else if (marker == OSUMarker) {
+ return new ScanInfo(
+ source.readLong(),
+ source.readInt(),
+ source.readString(),
+ source.readString(),
+ source.readString(),
+ source.readString(),
+ source.createByteArray(),
+ source.readInt()
+ );
+ }
+ else {
+ throw new RuntimeException("Bad ScanInfo data");
+ }
+ }
+
+ @Override
+ public ScanInfo[] newArray(int size) {
+ return new ScanInfo[0];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mScanResult != null) {
+ dest.writeInt(ScanResultMarker);
+ mScanResult.writeToParcel(dest, flags);
+ return;
+ }
+
+ dest.writeInt(OSUMarker);
+ dest.writeLong(mBSSID);
+ dest.writeInt(mRSSI);
+ dest.writeString(mSSID);
+ dest.writeString(mName);
+ dest.writeString(mServiceDescription);
+ dest.writeString(mIconType);
+ dest.writeByteArray(mIconData);
+ dest.writeInt(mOSUIdentity);
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index cf88df4..ff8d6d4d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1315,6 +1315,30 @@
}
/**
+ * An augmented version of getScanResults that returns ScanResults as well as OSU information
+ * wrapped in ScanInfo objects.
+ * @return
+ */
+ public List<ScanInfo> getScanInfos() {
+ try {
+ return mService.getScanInfos(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Notify the OSU framework about the currently selected OSU.
+ * @param osuID The OSU ID from ScanInfo.getOsuIdentity()
+ */
+ public void setOsuSelection(int osuID) {
+ try {
+ mService.setOsuSelection(osuID);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Check if scanning is always available.
*
* If this return {@code true}, apps can issue {@link #startScan} and fetch scan results