Merge "Support for Telecom Call IDs."
diff --git a/Android.mk b/Android.mk
index 40da134..d48887d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -160,6 +160,7 @@
core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl \
core/java/android/hardware/fingerprint/IFingerprintDaemonCallback.aidl \
core/java/android/hardware/fingerprint/IFingerprintService.aidl \
+ core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl \
core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl \
core/java/android/hardware/hdmi/IHdmiControlCallback.aidl \
core/java/android/hardware/hdmi/IHdmiControlService.aidl \
@@ -172,6 +173,7 @@
core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \
core/java/android/hardware/input/IInputManager.aidl \
core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
+ core/java/android/hardware/input/ITabletModeChangedListener.aidl \
core/java/android/hardware/location/IActivityRecognitionHardware.aidl \
core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl \
core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl \
@@ -346,6 +348,7 @@
media/java/android/media/IRingtonePlayer.aidl \
media/java/android/media/IVolumeController.aidl \
media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \
+ media/java/android/media/midi/IBluetoothMidiService.aidl \
media/java/android/media/midi/IMidiDeviceListener.aidl \
media/java/android/media/midi/IMidiDeviceOpenCallback.aidl \
media/java/android/media/midi/IMidiDeviceServer.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 48be749..6e44d77 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -235,6 +235,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinputflingerhost_intermediates $(PRODUCT_OUT)/obj_arm/SHARED_LIBRARIES/libinputflingerhost_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/target/common/obj/framework.aidl)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/DocumentsUI_intermediates)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index e9c5727..4f181c9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -241,10 +241,10 @@
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba
field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
- field public static final int activityHeight = 16844019; // 0x10104f3
+ field public static final int activityHeight = 16844021; // 0x10104f5
field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
- field public static final int activityWidth = 16844018; // 0x10104f2
+ field public static final int activityWidth = 16844020; // 0x10104f4
field public static final int addPrintersActivity = 16843750; // 0x10103e6
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
@@ -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
@@ -786,6 +788,7 @@
field public static final int listChoiceIndicatorSingle = 16843289; // 0x1010219
field public static final int listDivider = 16843284; // 0x1010214
field public static final int listDividerAlertDialog = 16843525; // 0x1010305
+ field public static final int listMenuViewStyle = 16844018; // 0x10104f2
field public static final int listPopupWindowStyle = 16843519; // 0x10102ff
field public static final int listPreferredItemHeight = 16842829; // 0x101004d
field public static final int listPreferredItemHeightLarge = 16843654; // 0x1010386
@@ -808,6 +811,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
@@ -991,6 +995,7 @@
field public static final int resizeClip = 16843983; // 0x10104cf
field public static final int resizeMode = 16843619; // 0x1010363
field public static final int resizeable = 16843405; // 0x101028d
+ field public static final int resizeableActivity = 16844022; // 0x10104f6
field public static final int resource = 16842789; // 0x1010025
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
@@ -1148,6 +1153,7 @@
field public static final int strokeLineJoin = 16843788; // 0x101040c
field public static final int strokeMiterLimit = 16843789; // 0x101040d
field public static final int strokeWidth = 16843783; // 0x1010407
+ field public static final int subMenuArrow = 16844019; // 0x10104f3
field public static final int submitBackground = 16843912; // 0x1010488
field public static final int subtitle = 16843473; // 0x10102d1
field public static final int subtitleTextAppearance = 16843823; // 0x101042f
@@ -1281,6 +1287,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
@@ -3477,14 +3488,14 @@
method public void setImmersive(boolean);
method public void setIntent(android.content.Intent);
method public final void setMediaController(android.media.session.MediaController);
- method public final void setProgress(int);
- method public final void setProgressBarIndeterminate(boolean);
- method public final void setProgressBarIndeterminateVisibility(boolean);
- method public final void setProgressBarVisibility(boolean);
+ method public final deprecated void setProgress(int);
+ method public final deprecated void setProgressBarIndeterminate(boolean);
+ method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
+ method public final deprecated void setProgressBarVisibility(boolean);
method public void setRequestedOrientation(int);
method public final void setResult(int);
method public final void setResult(int, android.content.Intent);
- method public final void setSecondaryProgress(int);
+ method public final deprecated void setSecondaryProgress(int);
method public void setTaskDescription(android.app.ActivityManager.TaskDescription);
method public void setTitle(java.lang.CharSequence);
method public void setTitle(int);
@@ -9663,12 +9674,14 @@
method public int diff(android.content.res.Configuration);
method public boolean equals(android.content.res.Configuration);
method public int getLayoutDirection();
+ method public android.util.LocaleList getLocales();
method public boolean isLayoutSizeAtLeast(int);
method public boolean isScreenRound();
method public static boolean needNewResources(int, int);
method public void readFromParcel(android.os.Parcel);
method public void setLayoutDirection(java.util.Locale);
method public void setLocale(java.util.Locale);
+ method public void setLocales(android.util.LocaleList);
method public void setTo(android.content.res.Configuration);
method public void setToDefaults();
method public int updateFrom(android.content.res.Configuration);
@@ -9742,7 +9755,7 @@
field public int hardKeyboardHidden;
field public int keyboard;
field public int keyboardHidden;
- field public java.util.Locale locale;
+ field public deprecated java.util.Locale locale;
field public int mcc;
field public int mnc;
field public int navigation;
@@ -11586,6 +11599,7 @@
method public void getTextBounds(java.lang.String, int, int, android.graphics.Rect);
method public void getTextBounds(char[], int, int, android.graphics.Rect);
method public java.util.Locale getTextLocale();
+ method public android.util.LocaleList getTextLocales();
method public void getTextPath(char[], int, int, float, float, android.graphics.Path);
method public void getTextPath(java.lang.String, int, int, float, float, android.graphics.Path);
method public float getTextScaleX();
@@ -11641,6 +11655,7 @@
method public void setSubpixelText(boolean);
method public void setTextAlign(android.graphics.Paint.Align);
method public void setTextLocale(java.util.Locale);
+ method public void setTextLocales(android.util.LocaleList);
method public void setTextScaleX(float);
method public void setTextSize(float);
method public void setTextSkewX(float);
@@ -18164,6 +18179,7 @@
method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(int);
method public android.mtp.MtpObjectInfo.Builder setKeywords(java.lang.String);
method public android.mtp.MtpObjectInfo.Builder setName(java.lang.String);
+ method public android.mtp.MtpObjectInfo.Builder setObjectHandle(int);
method public android.mtp.MtpObjectInfo.Builder setParent(int);
method public android.mtp.MtpObjectInfo.Builder setProtectionStatus(int);
method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(int);
@@ -34200,11 +34216,16 @@
public final class LocaleList {
ctor public LocaleList();
+ ctor public LocaleList(java.util.Locale);
ctor public LocaleList(java.util.Locale[]);
+ method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public static android.util.LocaleList getDefault();
+ method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getPrimary();
method public boolean isEmpty();
method public int size();
+ method public java.lang.String toLanguageTags();
}
public final class Log {
@@ -35352,6 +35373,7 @@
field public static final int KEYCODE_SLEEP = 223; // 0xdf
field public static final int KEYCODE_SOFT_LEFT = 1; // 0x1
field public static final int KEYCODE_SOFT_RIGHT = 2; // 0x2
+ field public static final int KEYCODE_SOFT_SLEEP = 276; // 0x114
field public static final int KEYCODE_SPACE = 62; // 0x3e
field public static final int KEYCODE_STAR = 17; // 0x11
field public static final int KEYCODE_STB_INPUT = 180; // 0xb4
@@ -37360,23 +37382,23 @@
field public static final int FEATURE_CONTENT_TRANSITIONS = 12; // 0xc
field public static final int FEATURE_CONTEXT_MENU = 6; // 0x6
field public static final int FEATURE_CUSTOM_TITLE = 7; // 0x7
- field public static final int FEATURE_INDETERMINATE_PROGRESS = 5; // 0x5
+ field public static final deprecated int FEATURE_INDETERMINATE_PROGRESS = 5; // 0x5
field public static final int FEATURE_LEFT_ICON = 3; // 0x3
field public static final int FEATURE_NO_TITLE = 1; // 0x1
field public static final int FEATURE_OPTIONS_PANEL = 0; // 0x0
- field public static final int FEATURE_PROGRESS = 2; // 0x2
+ field public static final deprecated int FEATURE_PROGRESS = 2; // 0x2
field public static final int FEATURE_RIGHT_ICON = 4; // 0x4
field public static final int FEATURE_SWIPE_TO_DISMISS = 11; // 0xb
field public static final int ID_ANDROID_CONTENT = 16908290; // 0x1020002
field public static final java.lang.String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = "android:navigation:background";
- field public static final int PROGRESS_END = 10000; // 0x2710
- field public static final int PROGRESS_INDETERMINATE_OFF = -4; // 0xfffffffc
- field public static final int PROGRESS_INDETERMINATE_ON = -3; // 0xfffffffd
- field public static final int PROGRESS_SECONDARY_END = 30000; // 0x7530
- field public static final int PROGRESS_SECONDARY_START = 20000; // 0x4e20
- field public static final int PROGRESS_START = 0; // 0x0
- field public static final int PROGRESS_VISIBILITY_OFF = -2; // 0xfffffffe
- field public static final int PROGRESS_VISIBILITY_ON = -1; // 0xffffffff
+ field public static final deprecated int PROGRESS_END = 10000; // 0x2710
+ field public static final deprecated int PROGRESS_INDETERMINATE_OFF = -4; // 0xfffffffc
+ field public static final deprecated int PROGRESS_INDETERMINATE_ON = -3; // 0xfffffffd
+ field public static final deprecated int PROGRESS_SECONDARY_END = 30000; // 0x7530
+ field public static final deprecated int PROGRESS_SECONDARY_START = 20000; // 0x4e20
+ field public static final deprecated int PROGRESS_START = 0; // 0x0
+ field public static final deprecated int PROGRESS_VISIBILITY_OFF = -2; // 0xfffffffe
+ field public static final deprecated int PROGRESS_VISIBILITY_ON = -1; // 0xffffffff
field public static final java.lang.String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background";
}
@@ -41712,6 +41734,7 @@
method public java.lang.CharSequence getText();
method public final android.content.res.ColorStateList getTextColors();
method public java.util.Locale getTextLocale();
+ method public android.util.LocaleList getTextLocales();
method public float getTextScaleX();
method public float getTextSize();
method public int getTotalPaddingBottom();
@@ -41824,6 +41847,7 @@
method public final void setTextKeepState(java.lang.CharSequence);
method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType);
method public void setTextLocale(java.util.Locale);
+ method public void setTextLocales(android.util.LocaleList);
method public void setTextScaleX(float);
method public void setTextSize(float);
method public void setTextSize(int, float);
@@ -41930,6 +41954,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);
@@ -41955,6 +41983,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 e234970..18fcb31 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -333,10 +333,10 @@
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba
field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
- field public static final int activityHeight = 16844019; // 0x10104f3
+ field public static final int activityHeight = 16844021; // 0x10104f5
field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
- field public static final int activityWidth = 16844018; // 0x10104f2
+ field public static final int activityWidth = 16844020; // 0x10104f4
field public static final int addPrintersActivity = 16843750; // 0x10103e6
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
@@ -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
@@ -878,6 +880,7 @@
field public static final int listChoiceIndicatorSingle = 16843289; // 0x1010219
field public static final int listDivider = 16843284; // 0x1010214
field public static final int listDividerAlertDialog = 16843525; // 0x1010305
+ field public static final int listMenuViewStyle = 16844018; // 0x10104f2
field public static final int listPopupWindowStyle = 16843519; // 0x10102ff
field public static final int listPreferredItemHeight = 16842829; // 0x101004d
field public static final int listPreferredItemHeightLarge = 16843654; // 0x1010386
@@ -900,6 +903,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
@@ -1083,6 +1087,7 @@
field public static final int resizeClip = 16843983; // 0x10104cf
field public static final int resizeMode = 16843619; // 0x1010363
field public static final int resizeable = 16843405; // 0x101028d
+ field public static final int resizeableActivity = 16844022; // 0x10104f6
field public static final int resource = 16842789; // 0x1010025
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
@@ -1244,6 +1249,7 @@
field public static final int strokeLineJoin = 16843788; // 0x101040c
field public static final int strokeMiterLimit = 16843789; // 0x101040d
field public static final int strokeWidth = 16843783; // 0x1010407
+ field public static final int subMenuArrow = 16844019; // 0x10104f3
field public static final int submitBackground = 16843912; // 0x1010488
field public static final int subtitle = 16843473; // 0x10102d1
field public static final int subtitleTextAppearance = 16843823; // 0x101042f
@@ -1377,6 +1383,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
@@ -3580,14 +3591,14 @@
method public void setImmersive(boolean);
method public void setIntent(android.content.Intent);
method public final void setMediaController(android.media.session.MediaController);
- method public final void setProgress(int);
- method public final void setProgressBarIndeterminate(boolean);
- method public final void setProgressBarIndeterminateVisibility(boolean);
- method public final void setProgressBarVisibility(boolean);
+ method public final deprecated void setProgress(int);
+ method public final deprecated void setProgressBarIndeterminate(boolean);
+ method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
+ method public final deprecated void setProgressBarVisibility(boolean);
method public void setRequestedOrientation(int);
method public final void setResult(int);
method public final void setResult(int, android.content.Intent);
- method public final void setSecondaryProgress(int);
+ method public final deprecated void setSecondaryProgress(int);
method public void setTaskDescription(android.app.ActivityManager.TaskDescription);
method public void setTitle(java.lang.CharSequence);
method public void setTitle(int);
@@ -10000,12 +10011,14 @@
method public int diff(android.content.res.Configuration);
method public boolean equals(android.content.res.Configuration);
method public int getLayoutDirection();
+ method public android.util.LocaleList getLocales();
method public boolean isLayoutSizeAtLeast(int);
method public boolean isScreenRound();
method public static boolean needNewResources(int, int);
method public void readFromParcel(android.os.Parcel);
method public void setLayoutDirection(java.util.Locale);
method public void setLocale(java.util.Locale);
+ method public void setLocales(android.util.LocaleList);
method public void setTo(android.content.res.Configuration);
method public void setToDefaults();
method public int updateFrom(android.content.res.Configuration);
@@ -10079,7 +10092,7 @@
field public int hardKeyboardHidden;
field public int keyboard;
field public int keyboardHidden;
- field public java.util.Locale locale;
+ field public deprecated java.util.Locale locale;
field public int mcc;
field public int mnc;
field public int navigation;
@@ -11923,6 +11936,7 @@
method public void getTextBounds(java.lang.String, int, int, android.graphics.Rect);
method public void getTextBounds(char[], int, int, android.graphics.Rect);
method public java.util.Locale getTextLocale();
+ method public android.util.LocaleList getTextLocales();
method public void getTextPath(char[], int, int, float, float, android.graphics.Path);
method public void getTextPath(java.lang.String, int, int, float, float, android.graphics.Path);
method public float getTextScaleX();
@@ -11978,6 +11992,7 @@
method public void setSubpixelText(boolean);
method public void setTextAlign(android.graphics.Paint.Align);
method public void setTextLocale(java.util.Locale);
+ method public void setTextLocales(android.util.LocaleList);
method public void setTextScaleX(float);
method public void setTextSize(float);
method public void setTextSkewX(float);
@@ -19676,6 +19691,7 @@
method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(int);
method public android.mtp.MtpObjectInfo.Builder setKeywords(java.lang.String);
method public android.mtp.MtpObjectInfo.Builder setName(java.lang.String);
+ method public android.mtp.MtpObjectInfo.Builder setObjectHandle(int);
method public android.mtp.MtpObjectInfo.Builder setParent(int);
method public android.mtp.MtpObjectInfo.Builder setProtectionStatus(int);
method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(int);
@@ -36494,11 +36510,16 @@
public final class LocaleList {
ctor public LocaleList();
+ ctor public LocaleList(java.util.Locale);
ctor public LocaleList(java.util.Locale[]);
+ method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public static android.util.LocaleList getDefault();
+ method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getPrimary();
method public boolean isEmpty();
method public int size();
+ method public java.lang.String toLanguageTags();
}
public final class Log {
@@ -37646,6 +37667,7 @@
field public static final int KEYCODE_SLEEP = 223; // 0xdf
field public static final int KEYCODE_SOFT_LEFT = 1; // 0x1
field public static final int KEYCODE_SOFT_RIGHT = 2; // 0x2
+ field public static final int KEYCODE_SOFT_SLEEP = 276; // 0x114
field public static final int KEYCODE_SPACE = 62; // 0x3e
field public static final int KEYCODE_STAR = 17; // 0x11
field public static final int KEYCODE_STB_INPUT = 180; // 0xb4
@@ -39655,23 +39677,23 @@
field public static final int FEATURE_CONTENT_TRANSITIONS = 12; // 0xc
field public static final int FEATURE_CONTEXT_MENU = 6; // 0x6
field public static final int FEATURE_CUSTOM_TITLE = 7; // 0x7
- field public static final int FEATURE_INDETERMINATE_PROGRESS = 5; // 0x5
+ field public static final deprecated int FEATURE_INDETERMINATE_PROGRESS = 5; // 0x5
field public static final int FEATURE_LEFT_ICON = 3; // 0x3
field public static final int FEATURE_NO_TITLE = 1; // 0x1
field public static final int FEATURE_OPTIONS_PANEL = 0; // 0x0
- field public static final int FEATURE_PROGRESS = 2; // 0x2
+ field public static final deprecated int FEATURE_PROGRESS = 2; // 0x2
field public static final int FEATURE_RIGHT_ICON = 4; // 0x4
field public static final int FEATURE_SWIPE_TO_DISMISS = 11; // 0xb
field public static final int ID_ANDROID_CONTENT = 16908290; // 0x1020002
field public static final java.lang.String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = "android:navigation:background";
- field public static final int PROGRESS_END = 10000; // 0x2710
- field public static final int PROGRESS_INDETERMINATE_OFF = -4; // 0xfffffffc
- field public static final int PROGRESS_INDETERMINATE_ON = -3; // 0xfffffffd
- field public static final int PROGRESS_SECONDARY_END = 30000; // 0x7530
- field public static final int PROGRESS_SECONDARY_START = 20000; // 0x4e20
- field public static final int PROGRESS_START = 0; // 0x0
- field public static final int PROGRESS_VISIBILITY_OFF = -2; // 0xfffffffe
- field public static final int PROGRESS_VISIBILITY_ON = -1; // 0xffffffff
+ field public static final deprecated int PROGRESS_END = 10000; // 0x2710
+ field public static final deprecated int PROGRESS_INDETERMINATE_OFF = -4; // 0xfffffffc
+ field public static final deprecated int PROGRESS_INDETERMINATE_ON = -3; // 0xfffffffd
+ field public static final deprecated int PROGRESS_SECONDARY_END = 30000; // 0x7530
+ field public static final deprecated int PROGRESS_SECONDARY_START = 20000; // 0x4e20
+ field public static final deprecated int PROGRESS_START = 0; // 0x0
+ field public static final deprecated int PROGRESS_VISIBILITY_OFF = -2; // 0xfffffffe
+ field public static final deprecated int PROGRESS_VISIBILITY_ON = -1; // 0xffffffff
field public static final java.lang.String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background";
}
@@ -44320,6 +44342,7 @@
method public java.lang.CharSequence getText();
method public final android.content.res.ColorStateList getTextColors();
method public java.util.Locale getTextLocale();
+ method public android.util.LocaleList getTextLocales();
method public float getTextScaleX();
method public float getTextSize();
method public int getTotalPaddingBottom();
@@ -44432,6 +44455,7 @@
method public final void setTextKeepState(java.lang.CharSequence);
method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType);
method public void setTextLocale(java.util.Locale);
+ method public void setTextLocales(android.util.LocaleList);
method public void setTextScaleX(float);
method public void setTextSize(float);
method public void setTextSize(int, float);
@@ -44538,6 +44562,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);
@@ -44563,6 +44591,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..9185d7a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -2266,12 +2266,12 @@
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);
+ mAm.resizeTask(taskId, bounds, pretendUserResize);
Thread.sleep(delay_ms);
} catch (RemoteException e) {
System.err.println("Error changing task bounds: " + e);
@@ -2354,7 +2354,7 @@
taskRect.top += maxMove;
taskRect.bottom += maxMove;
}
- taskResize(taskId, taskRect, delay_ms);
+ taskResize(taskId, taskRect, delay_ms, false);
}
} else {
while (maxToTravel < 0
@@ -2371,7 +2371,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 +2405,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 +2418,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 +2431,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 +2444,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 +2457,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 +2470,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 +2483,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 +2496,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);
}
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 471fa3b..214dc5d 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -51,7 +51,8 @@
out.println(
"usage: dpm [subcommand] [options]\n" +
"usage: dpm set-active-admin [ --user <USER_ID> ] <COMPONENT>\n" +
- "usage: dpm set-device-owner <COMPONENT>\n" +
+ // STOPSHIP Finalize it
+ "usage: dpm set-device-owner [ --user <USER_ID> *EXPERIMENTAL* ] <COMPONENT>\n" +
"usage: dpm set-profile-owner [ --user <USER_ID> ] <COMPONENT>\n" +
"\n" +
"dpm set-active-admin: Sets the given component as active admin" +
@@ -106,22 +107,22 @@
}
private void runSetDeviceOwner() throws RemoteException {
- ComponentName component = parseComponentName(nextArgRequired());
- mDevicePolicyManager.setActiveAdmin(component, true /*refreshing*/, UserHandle.USER_SYSTEM);
+ parseArgs(true);
+ mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
- String packageName = component.getPackageName();
+ String packageName = mComponent.getPackageName();
try {
- if (!mDevicePolicyManager.setDeviceOwner(packageName, null /*ownerName*/)) {
+ if (!mDevicePolicyManager.setDeviceOwner(packageName, null /*ownerName*/, mUserId)) {
throw new RuntimeException(
"Can't set package " + packageName + " as device owner.");
}
} catch (Exception e) {
// Need to remove the admin that we just added.
- mDevicePolicyManager.removeActiveAdmin(component, UserHandle.USER_SYSTEM);
+ mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
throw e;
}
System.out.println("Success: Device owner set to package " + packageName);
- System.out.println("Active admin set to component " + component.toShortString());
+ System.out.println("Active admin set to component " + mComponent.toShortString());
}
private void runSetProfileOwner() throws RemoteException {
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..d751f96f 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -426,7 +426,7 @@
@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);
@@ -451,7 +451,7 @@
@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);
@@ -468,7 +468,7 @@
*/
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);
@@ -485,7 +485,8 @@
*/
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);
@@ -522,7 +523,8 @@
/** @hide Same as {@link #getAccountsByType(String)} but for a specific user. */
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 +612,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 +664,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)) {
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 04b3c88..4378df4 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);
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 5b88c8e..16f825d 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -408,15 +408,18 @@
/**
* Returns true if any of the child animations of this AnimatorSet have been started and have
- * not yet ended.
- * @return Whether this AnimatorSet has been started and has not yet ended.
+ * not yet ended. Child animations will not be started until the AnimatorSet has gone past
+ * its initial delay set through {@link #setStartDelay(long)}.
+ *
+ * @return Whether this AnimatorSet has gone past the initial delay, and at least one child
+ * animation has been started and not yet ended.
*/
@Override
public boolean isRunning() {
int size = mNodes.size();
for (int i = 0; i < size; i++) {
Node node = mNodes.get(i);
- if (node != mRootNode && node.mAnimation.isRunning()) {
+ if (node != mRootNode && node.mAnimation.isStarted()) {
return true;
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a4e1135..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 */);
}
/**
@@ -5436,7 +5409,9 @@
* via {@link #requestWindowFeature(int)}.
*
* @param visible Whether to show the progress bars in the title.
+ * @deprecated No longer supported starting in API 21.
*/
+ @Deprecated
public final void setProgressBarVisibility(boolean visible) {
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, visible ? Window.PROGRESS_VISIBILITY_ON :
Window.PROGRESS_VISIBILITY_OFF);
@@ -5449,7 +5424,9 @@
* via {@link #requestWindowFeature(int)}.
*
* @param visible Whether to show the progress bars in the title.
+ * @deprecated No longer supported starting in API 21.
*/
+ @Deprecated
public final void setProgressBarIndeterminateVisibility(boolean visible) {
getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF);
@@ -5463,7 +5440,9 @@
* via {@link #requestWindowFeature(int)}.
*
* @param indeterminate Whether the horizontal progress bar should be indeterminate.
+ * @deprecated No longer supported starting in API 21.
*/
+ @Deprecated
public final void setProgressBarIndeterminate(boolean indeterminate) {
getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
indeterminate ? Window.PROGRESS_INDETERMINATE_ON
@@ -5479,7 +5458,9 @@
* @param progress The progress for the progress bar. Valid ranges are from
* 0 to 10000 (both inclusive). If 10000 is given, the progress
* bar will be completely filled and will fade out.
+ * @deprecated No longer supported starting in API 21.
*/
+ @Deprecated
public final void setProgress(int progress) {
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START);
}
@@ -5496,7 +5477,9 @@
*
* @param secondaryProgress The secondary progress for the progress bar. Valid ranges are from
* 0 to 10000 (both inclusive).
+ * @deprecated No longer supported starting in API 21.
*/
+ @Deprecated
public final void setSecondaryProgress(int secondaryProgress) {
getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
secondaryProgress + Window.PROGRESS_SECONDARY_START);
@@ -6241,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);
@@ -6335,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 580f721..9ef51c8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -452,6 +452,22 @@
*/
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;
+
/** @hide */
public int getFrontActivityScreenCompatMode() {
try {
@@ -1108,7 +1124,7 @@
* of {@link #RECENT_WITH_EXCLUDED} and {@link #RECENT_IGNORE_UNAVAILABLE}.
*
* @return Returns a list of RecentTaskInfo records describing each of
- * the recent tasks.
+ * the recent tasks. Most recently activated tasks go first.
*
* @hide
*/
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1f1f356..da6fc59 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();
+ final boolean resizedByUser = data.readInt() == 1;
Rect r = Rect.CREATOR.createFromParcel(data);
- resizeTask(taskId, r);
+ resizeTask(taskId, r, resizedByUser);
reply.writeNoException();
return true;
}
@@ -3536,6 +3521,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 +3631,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 +5900,13 @@
}
@Override
- public void resizeTask(int taskId, Rect r) throws RemoteException
+ public void resizeTask(int taskId, Rect r, boolean resizedByUser) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(taskId);
+ data.writeInt(resizedByUser ? 1 : 0);
r.writeToParcel(data, 0);
mRemote.transact(RESIZE_TASK_TRANSACTION, data, reply, 0);
reply.readException();
@@ -5941,35 +5930,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();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 2406985..933f98d 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
@@ -59,6 +60,13 @@
public static final String KEY_PACKAGE_NAME = "android:activity.packageName";
/**
+ * The bounds that the activity should be started in. Set to null explicitly
+ * for full screen. If the key is not found, previous bounds will be preserved.
+ * @hide
+ */
+ public static final String KEY_BOUNDS = "android:activity.bounds";
+
+ /**
* Type of animation that arguments specify.
* @hide
*/
@@ -163,6 +171,8 @@
public static final int ANIM_CLIP_REVEAL = 11;
private String mPackageName;
+ private boolean mHasBounds;
+ private Rect mBounds;
private int mAnimationType = ANIM_NONE;
private int mCustomEnterResId;
private int mCustomExitResId;
@@ -631,6 +641,10 @@
} catch (RuntimeException e) {
Slog.w(TAG, e);
}
+ mHasBounds = opts.containsKey(KEY_BOUNDS);
+ if (mHasBounds) {
+ mBounds = opts.getParcelable(KEY_BOUNDS);
+ }
mAnimationType = opts.getInt(KEY_ANIM_TYPE);
switch (mAnimationType) {
case ANIM_CUSTOM:
@@ -677,11 +691,28 @@
}
/** @hide */
+ public ActivityOptions setBounds(Rect bounds) {
+ mHasBounds = true;
+ mBounds = bounds;
+ return this;
+ }
+
+ /** @hide */
public String getPackageName() {
return mPackageName;
}
/** @hide */
+ public boolean hasBounds() {
+ return mHasBounds;
+ }
+
+ /** @hide */
+ public Rect getBounds(){
+ return mBounds;
+ }
+
+ /** @hide */
public int getAnimationType() {
return mAnimationType;
}
@@ -867,6 +898,9 @@
if (mPackageName != null) {
b.putString(KEY_PACKAGE_NAME, mPackageName);
}
+ if (mHasBounds) {
+ b.putParcelable(KEY_BOUNDS, mBounds);
+ }
b.putInt(KEY_ANIM_TYPE, mAnimationType);
if (mUsageTimeReport != null) {
b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 412e3cd..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;
@@ -4593,27 +4622,6 @@
}
updateDefaultDensity();
- final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
- if (!Process.isIsolated()) {
- final File cacheDir = appContext.getCacheDir();
-
- if (cacheDir != null) {
- // Provide a usable directory for temporary files
- System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
- } else {
- Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property due to missing cache directory");
- }
-
- // Use codeCacheDir to store generated/compiled graphics code
- final File codeCacheDir = appContext.getCodeCacheDir();
- if (codeCacheDir != null) {
- setupGraphicsSupport(data.info, codeCacheDir);
- } else {
- Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory");
- }
- }
-
-
final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24));
DateFormat.set24HourTimePref(is24Hr);
@@ -4685,29 +4693,28 @@
/**
* Initialize the default http proxy in this process for the reasons we set the time zone.
*/
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
if (b != null) {
// In pre-boot mode (doing initial launch to collect password), not
// all system is up. This includes the connectivity service, so don't
// crash if we can't get it.
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ final IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
Proxy.setHttpProxySystemProperty(proxyInfo);
} catch (RemoteException e) {}
}
+ // Instrumentation info affects the class loader, so load it before
+ // setting up the app context.
+ final InstrumentationInfo ii;
if (data.instrumentationName != null) {
- InstrumentationInfo ii = null;
try {
- ii = appContext.getPackageManager().
- getInstrumentationInfo(data.instrumentationName, 0);
+ ii = new ApplicationPackageManager(null, getPackageManager())
+ .getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
- }
- if (ii == null) {
throw new RuntimeException(
- "Unable to find instrumentation info for: "
- + data.instrumentationName);
+ "Unable to find instrumentation info for: " + data.instrumentationName);
}
mInstrumentationPackageName = ii.packageName;
@@ -4717,8 +4724,33 @@
mInstrumentedAppDir = data.info.getAppDir();
mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
mInstrumentedLibDir = data.info.getLibDir();
+ } else {
+ ii = null;
+ }
- ApplicationInfo instrApp = new ApplicationInfo();
+ final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
+ if (!Process.isIsolated()) {
+ final File cacheDir = appContext.getCacheDir();
+ if (cacheDir != null) {
+ // Provide a usable directory for temporary files
+ System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+ } else {
+ Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property "
+ + "due to missing cache directory");
+ }
+
+ // Use codeCacheDir to store generated/compiled graphics code
+ final File codeCacheDir = appContext.getCodeCacheDir();
+ if (codeCacheDir != null) {
+ setupGraphicsSupport(data.info, codeCacheDir);
+ } else {
+ Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory");
+ }
+ }
+
+ // Continue loading instrumentation.
+ if (ii != null) {
+ final ApplicationInfo instrApp = new ApplicationInfo();
instrApp.packageName = ii.packageName;
instrApp.sourceDir = ii.sourceDir;
instrApp.publicSourceDir = ii.publicSourceDir;
@@ -4726,12 +4758,13 @@
instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs;
instrApp.dataDir = ii.dataDir;
instrApp.nativeLibraryDir = ii.nativeLibraryDir;
- LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
+
+ final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
- ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
+ final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try {
- java.lang.ClassLoader cl = instrContext.getClassLoader();
+ final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
@@ -4740,18 +4773,17 @@
+ data.instrumentationName + ": " + e.toString(), e);
}
- mInstrumentation.init(this, instrContext, appContext,
- new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
- data.instrumentationUiAutomationConnection);
+ final ComponentName component = new ComponentName(ii.packageName, ii.name);
+ mInstrumentation.init(this, instrContext, appContext, component,
+ data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
if (mProfiler.profileFile != null && !ii.handleProfiling
&& mProfiler.profileFd == null) {
mProfiler.handlingProfiling = true;
- File file = new File(mProfiler.profileFile);
+ final File file = new File(mProfiler.profileFile);
file.getParentFile().mkdirs();
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
}
-
} else {
mInstrumentation = new Instrumentation();
}
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/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/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..f70423f 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1166,6 +1166,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
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 3b026d2..132ffef 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -2196,6 +2196,7 @@
fragment.mTag = tag;
fragment.mInLayout = true;
fragment.mFragmentManager = this;
+ fragment.mHost = mHost;
fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
addFragment(fragment, true);
} else if (fragment.mInLayout) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 47abf26..7bd832b 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, boolean resizedByUser) throws RemoteException;
public Rect getTaskBounds(int taskId) throws RemoteException;
public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
@@ -891,7 +890,5 @@
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;
}
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/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index af3b565..181c907 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -256,10 +256,10 @@
protected void drawableStateChanged() {
super.drawableStateChanged();
- if (mRemoteIndicator != null) {
- int[] myDrawableState = getDrawableState();
- mRemoteIndicator.setState(myDrawableState);
- invalidate();
+ final Drawable remoteIndicator = mRemoteIndicator;
+ if (remoteIndicator != null && remoteIndicator.isStateful()
+ && remoteIndicator.setState(getDrawableState())) {
+ invalidateDrawable(remoteIndicator);
}
}
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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e3414d9..ac50699 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -213,7 +213,8 @@
* @see DeviceAdminReceiver
* @deprecated Use {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}. This extra is still
- * supported.
+ * supported, but only if there is only one device admin receiver in the package that requires
+ * the permission {@link android.Manifest.permission#BIND_DEVICE_ADMIN}.
*/
@Deprecated
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME
@@ -2683,13 +2684,26 @@
* @throws IllegalArgumentException if the package name is null or invalid
* @throws IllegalStateException If the preconditions mentioned are not met.
*/
- public boolean setDeviceOwner(String packageName) throws IllegalArgumentException,
- IllegalStateException {
+ public boolean setDeviceOwner(String packageName) {
return setDeviceOwner(packageName, null);
}
/**
* @hide
+ */
+ public boolean setDeviceOwner(String packageName, int userId) {
+ return setDeviceOwner(packageName, null, userId);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean setDeviceOwner(String packageName, String ownerName) {
+ return setDeviceOwner(packageName, ownerName, UserHandle.USER_SYSTEM);
+ }
+
+ /**
+ * @hide
* Sets the given package as the device owner. The package must already be installed. There
* must not already be a device owner.
* Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
@@ -2698,15 +2712,16 @@
* the caller is the shell uid, and there are no additional users and no accounts.
* @param packageName the package name of the application to be registered as the device owner.
* @param ownerName the human readable name of the institution that owns this device.
+ * @param userId ID of the user on which the device owner runs.
* @return whether the package was successfully registered as the device owner.
* @throws IllegalArgumentException if the package name is null or invalid
* @throws IllegalStateException If the preconditions mentioned are not met.
*/
- public boolean setDeviceOwner(String packageName, String ownerName)
+ public boolean setDeviceOwner(String packageName, String ownerName, int userId)
throws IllegalArgumentException, IllegalStateException {
if (mService != null) {
try {
- return mService.setDeviceOwner(packageName, ownerName);
+ return mService.setDeviceOwner(packageName, ownerName, userId);
} catch (RemoteException re) {
Log.w(TAG, "Failed to set device owner");
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 376a3d8..55a21c6 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -113,7 +113,7 @@
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
- boolean setDeviceOwner(String packageName, String ownerName);
+ boolean setDeviceOwner(String packageName, String ownerName, int userId);
boolean isDeviceOwner(String packageName);
String getDeviceOwner();
String getDeviceOwnerName();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e659049..b08db20 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
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/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6feb860..914945b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -531,12 +531,14 @@
/**
* String retrieved from the seinfo tag found in selinux policy. This value
- * is useful in setting an SELinux security context on the process as well
- * as its data directory.
+ * can be overridden with a value set through the mac_permissions.xml policy
+ * construct. This value is useful in setting an SELinux security context on
+ * the process as well as its data directory. The String default is being used
+ * here to represent a catchall label when no policy matches.
*
* {@hide}
*/
- public String seinfo;
+ public String seinfo = "default";
/**
* Paths to all shared libraries this application is linked against. This
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/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 968f9b2..d7ecbfe 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4561,6 +4561,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/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index c248a9e..04c690b 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -610,14 +610,28 @@
* {@hide}
*/
public final int addAssetPath(String path) {
+ return addAssetPathInternal(path, false);
+ }
+
+ /**
+ * Add an application assets to the asset manager and loading it as shared library.
+ * This can be either a directory or ZIP file. Not for use by applications. Returns
+ * the cookie of the added asset, or 0 on failure.
+ * {@hide}
+ */
+ public final int addAssetPathAsSharedLibrary(String path) {
+ return addAssetPathInternal(path, true);
+ }
+
+ private final int addAssetPathInternal(String path, boolean appAsLib) {
synchronized (this) {
- int res = addAssetPathNative(path);
+ int res = addAssetPathNative(path, appAsLib);
makeStringBlocks(mStringBlocks);
return res;
}
}
- private native final int addAssetPathNative(String path);
+ private native final int addAssetPathNative(String path, boolean appAsLib);
/**
* Add a set of assets to overlay an already added set of assets.
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index fd60476..927c02f 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -22,11 +22,13 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.LocaleList;
import android.view.View;
import java.io.IOException;
@@ -36,7 +38,7 @@
/**
* This class describes all device configuration information that can
* impact the resources the application retrieves. This includes both
- * user-specified configuration options (locale and scaling) as well
+ * user-specified configuration options (locale list and scaling) as well
* as device configurations (such as input modes, screen size and screen orientation).
* <p>You can acquire this object from {@link Resources}, using {@link
* Resources#getConfiguration}. Thus, from an activity, you can get it by chaining the request
@@ -78,8 +80,13 @@
* Current user preference for the locale, corresponding to
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
* resource qualifier.
+ *
+ * @deprecated Do not set or read this directly. Use {@link #getLocales()} and
+ * {@link #setLocales(LocaleList)}.
*/
- public Locale locale;
+ @Deprecated public Locale locale;
+
+ private LocaleList mLocaleList;
/**
* Locale should persist on setting. This is hidden because it is really
@@ -648,6 +655,15 @@
setTo(o);
}
+ /* This brings mLocaleList in sync with locale in case a user of the older API who doesn't know
+ * about setLocales() has changed locale directly. */
+ private void fixUpLocaleList() {
+ if ((locale == null && !mLocaleList.isEmpty()) ||
+ (locale != null && !locale.equals(mLocaleList.getPrimary()))) {
+ mLocaleList = new LocaleList(locale);
+ }
+ }
+
public void setTo(Configuration o) {
fontScale = o.fontScale;
mcc = o.mcc;
@@ -655,6 +671,8 @@
if (o.locale != null) {
locale = (Locale) o.locale.clone();
}
+ o.fixUpLocaleList();
+ mLocaleList = o.mLocaleList;
userSetLocale = o.userSetLocale;
touchscreen = o.touchscreen;
keyboard = o.keyboard;
@@ -692,11 +710,12 @@
} else {
sb.append("?mnc");
}
- if (locale != null) {
+ fixUpLocaleList();
+ if (!mLocaleList.isEmpty()) {
sb.append(" ");
- sb.append(locale);
+ sb.append(mLocaleList);
} else {
- sb.append(" ?locale");
+ sb.append(" ?localeList");
}
int layoutDir = (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK);
switch (layoutDir) {
@@ -819,6 +838,7 @@
public void setToDefaults() {
fontScale = 1;
mcc = mnc = 0;
+ mLocaleList = LocaleList.getEmptyLocaleList();
locale = null;
userSetLocale = false;
touchscreen = TOUCHSCREEN_UNDEFINED;
@@ -864,16 +884,20 @@
changed |= ActivityInfo.CONFIG_MNC;
mnc = delta.mnc;
}
- if (delta.locale != null
- && (locale == null || !locale.equals(delta.locale))) {
+ fixUpLocaleList();
+ delta.fixUpLocaleList();
+ if (!delta.mLocaleList.isEmpty() && !mLocaleList.equals(delta.mLocaleList)) {
changed |= ActivityInfo.CONFIG_LOCALE;
- locale = delta.locale != null
- ? (Locale) delta.locale.clone() : null;
- // If locale has changed, then layout direction is also changed ...
- changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
- // ... and we need to update the layout direction (represented by the first
- // 2 most significant bits in screenLayout).
- setLayoutDirection(locale);
+ mLocaleList = delta.mLocaleList;
+ // delta.locale can't be null, since delta.mLocaleList is not empty.
+ if (!delta.locale.equals(locale)) {
+ locale = (Locale) delta.locale.clone();
+ // If locale has changed, then layout direction is also changed ...
+ changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+ // ... and we need to update the layout direction (represented by the first
+ // 2 most significant bits in screenLayout).
+ setLayoutDirection(locale);
+ }
}
final int deltaScreenLayoutDir = delta.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
if (deltaScreenLayoutDir != SCREENLAYOUT_LAYOUTDIR_UNDEFINED &&
@@ -1023,8 +1047,9 @@
if (delta.mnc != 0 && mnc != delta.mnc) {
changed |= ActivityInfo.CONFIG_MNC;
}
- if (delta.locale != null
- && (locale == null || !locale.equals(delta.locale))) {
+ fixUpLocaleList();
+ delta.fixUpLocaleList();
+ if (!delta.mLocaleList.isEmpty() && !mLocaleList.equals(delta.mLocaleList)) {
changed |= ActivityInfo.CONFIG_LOCALE;
changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
}
@@ -1146,14 +1171,15 @@
dest.writeFloat(fontScale);
dest.writeInt(mcc);
dest.writeInt(mnc);
- if (locale == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(1);
- dest.writeString(locale.getLanguage());
- dest.writeString(locale.getCountry());
- dest.writeString(locale.getVariant());
+
+ fixUpLocaleList();
+ final int localeListSize = mLocaleList.size();
+ dest.writeInt(localeListSize);
+ for (int i = 0; i < localeListSize; ++i) {
+ final Locale l = mLocaleList.get(i);
+ dest.writeString(l.toLanguageTag());
}
+
if(userSetLocale) {
dest.writeInt(1);
} else {
@@ -1182,10 +1208,15 @@
fontScale = source.readFloat();
mcc = source.readInt();
mnc = source.readInt();
- if (source.readInt() != 0) {
- locale = new Locale(source.readString(), source.readString(),
- source.readString());
+
+ final int localeListSize = source.readInt();
+ final Locale[] localeArray = new Locale[localeListSize];
+ for (int i = 0; i < localeListSize; ++i) {
+ localeArray[i] = Locale.forLanguageTag(source.readString());
}
+ mLocaleList = new LocaleList(localeArray);
+ locale = mLocaleList.getPrimary();
+
userSetLocale = (source.readInt()==1);
touchscreen = source.readInt();
keyboard = source.readInt();
@@ -1234,18 +1265,33 @@
if (n != 0) return n;
n = this.mnc - that.mnc;
if (n != 0) return n;
- if (this.locale == null) {
- if (that.locale != null) return 1;
- } else if (that.locale == null) {
+
+ fixUpLocaleList();
+ that.fixUpLocaleList();
+ // for backward compatibility, we consider an empty locale list to be greater
+ // than any non-empty locale list.
+ if (this.mLocaleList.isEmpty()) {
+ if (!that.mLocaleList.isEmpty()) return 1;
+ } else if (that.mLocaleList.isEmpty()) {
return -1;
} else {
- n = this.locale.getLanguage().compareTo(that.locale.getLanguage());
- if (n != 0) return n;
- n = this.locale.getCountry().compareTo(that.locale.getCountry());
- if (n != 0) return n;
- n = this.locale.getVariant().compareTo(that.locale.getVariant());
+ final int minSize = Math.min(this.mLocaleList.size(), that.mLocaleList.size());
+ for (int i = 0; i < minSize; ++i) {
+ final Locale thisLocale = this.mLocaleList.get(i);
+ final Locale thatLocale = that.mLocaleList.get(i);
+ n = thisLocale.getLanguage().compareTo(thatLocale.getLanguage());
+ if (n != 0) return n;
+ n = thisLocale.getCountry().compareTo(thatLocale.getCountry());
+ if (n != 0) return n;
+ n = thisLocale.getVariant().compareTo(thatLocale.getVariant());
+ if (n != 0) return n;
+ n = thisLocale.toLanguageTag().compareTo(thatLocale.toLanguageTag());
+ if (n != 0) return n;
+ }
+ n = this.mLocaleList.size() - that.mLocaleList.size();
if (n != 0) return n;
}
+
n = this.touchscreen - that.touchscreen;
if (n != 0) return n;
n = this.keyboard - that.keyboard;
@@ -1288,13 +1334,13 @@
}
return false;
}
-
+
public int hashCode() {
int result = 17;
result = 31 * result + Float.floatToIntBits(fontScale);
result = 31 * result + mcc;
result = 31 * result + mnc;
- result = 31 * result + (locale != null ? locale.hashCode() : 0);
+ result = 31 * result + mLocaleList.hashCode();
result = 31 * result + touchscreen;
result = 31 * result + keyboard;
result = 31 * result + keyboardHidden;
@@ -1312,14 +1358,47 @@
}
/**
- * Set the locale. This is the preferred way for setting up the locale (instead of using the
- * direct accessor). This will also set the layout direction according to the locale.
+ * Get the locale list. This is the preferred way for getting the locales (instead of using
+ * the direct accessor to {@link #locale}, which would only provide the primary locale).
+ *
+ * @return The locale list.
+ */
+ public LocaleList getLocales() {
+ fixUpLocaleList();
+ return mLocaleList;
+ }
+
+ /**
+ * Set the locale list. This is the preferred way for setting up the locales (instead of using
+ * the direct accessor or {@link #setLocale(Locale)}). This will also set the layout direction
+ * according to the first locale in the list.
+ *
+ * Note that the layout direction will always come from the first locale in the locale list,
+ * even if the locale is not supported by the resources (the resources may only support
+ * another locale further down the list which has a different direction).
+ *
+ * @param locales The locale list. If null, an empty LocaleList will be assigned.
+ */
+ public void setLocales(@Nullable LocaleList locales) {
+ mLocaleList = locales == null ? LocaleList.getEmptyLocaleList() : locales;
+ locale = mLocaleList.getPrimary();
+ setLayoutDirection(locale);
+ }
+
+ /**
+ * Set the locale list to a list of just one locale. This will also set the layout direction
+ * according to the locale.
+ *
+ * Note that after this is run, calling <code>.equals()</code> on the input locale and the
+ * {@link #locale} attribute would return <code>true</code> if they are not null, but there is
+ * no guarantee that they would be the same object.
+ *
+ * See also the note about layout direction in {@link #setLocales(LocaleList)}.
*
* @param loc The locale. Can be null.
*/
- public void setLocale(Locale loc) {
- locale = loc;
- setLayoutDirection(locale);
+ public void setLocale(@Nullable Locale loc) {
+ setLocales(new LocaleList(loc));
}
/**
@@ -1335,19 +1414,19 @@
}
/**
- * Set the layout direction from the Locale.
+ * Set the layout direction from a Locale.
*
- * @param locale The Locale. If null will set the layout direction to
+ * @param loc The Locale. If null will set the layout direction to
* {@link View#LAYOUT_DIRECTION_LTR}. If not null will set it to the layout direction
* corresponding to the Locale.
*
* @see View#LAYOUT_DIRECTION_LTR
* @see View#LAYOUT_DIRECTION_RTL
*/
- public void setLayoutDirection(Locale locale) {
+ public void setLayoutDirection(Locale loc) {
// There is a "1" difference between the configuration values for
// layout direction and View constants for layout direction, just add "1".
- final int layoutDirection = 1 + TextUtils.getLayoutDirectionFromLocale(locale);
+ final int layoutDirection = 1 + TextUtils.getLayoutDirectionFromLocale(loc);
screenLayout = (screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK)|
(layoutDirection << SCREENLAYOUT_LAYOUTDIR_SHIFT);
}
@@ -1370,21 +1449,21 @@
*
* @hide
*/
- public static String localeToResourceQualifier(Locale locale) {
+ public static String localeToResourceQualifier(Locale loc) {
StringBuilder sb = new StringBuilder();
- boolean l = (locale.getLanguage().length() != 0);
- boolean c = (locale.getCountry().length() != 0);
- boolean s = (locale.getScript().length() != 0);
- boolean v = (locale.getVariant().length() != 0);
-
+ boolean l = (loc.getLanguage().length() != 0);
+ boolean c = (loc.getCountry().length() != 0);
+ boolean s = (loc.getScript().length() != 0);
+ boolean v = (loc.getVariant().length() != 0);
+ // TODO: take script and extensions into account
if (l) {
- sb.append(locale.getLanguage());
+ sb.append(loc.getLanguage());
if (c) {
- sb.append("-r").append(locale.getCountry());
+ sb.append("-r").append(loc.getCountry());
if (s) {
- sb.append("-s").append(locale.getScript());
+ sb.append("-s").append(loc.getScript());
if (v) {
- sb.append("-v").append(locale.getVariant());
+ sb.append("-v").append(loc.getVariant());
}
}
}
@@ -1409,6 +1488,7 @@
}
}
+ // TODO: send the whole locale list
if (config.locale != null && !config.locale.getLanguage().isEmpty()) {
parts.add(localeToResourceQualifier(config.locale));
}
@@ -1646,8 +1726,10 @@
delta.mnc = change.mnc;
}
- if ((base.locale == null && change.locale != null) ||
- (base.locale != null && !base.locale.equals(change.locale))) {
+ base.fixUpLocaleList();
+ change.fixUpLocaleList();
+ if (!base.mLocaleList.equals(change.mLocaleList)) {
+ delta.mLocaleList = change.mLocaleList;
delta.locale = change.locale;
}
@@ -1724,7 +1806,7 @@
private static final String XML_ATTR_FONT_SCALE = "fs";
private static final String XML_ATTR_MCC = "mcc";
private static final String XML_ATTR_MNC = "mnc";
- private static final String XML_ATTR_LOCALE = "locale";
+ private static final String XML_ATTR_LOCALES = "locales";
private static final String XML_ATTR_TOUCHSCREEN = "touch";
private static final String XML_ATTR_KEYBOARD = "key";
private static final String XML_ATTR_KEYBOARD_HIDDEN = "keyHid";
@@ -1754,10 +1836,9 @@
configOut.mcc = XmlUtils.readIntAttribute(parser, XML_ATTR_MCC, 0);
configOut.mnc = XmlUtils.readIntAttribute(parser, XML_ATTR_MNC, 0);
- final String localeStr = XmlUtils.readStringAttribute(parser, XML_ATTR_LOCALE);
- if (localeStr != null) {
- configOut.locale = Locale.forLanguageTag(localeStr);
- }
+ final String localesStr = XmlUtils.readStringAttribute(parser, XML_ATTR_LOCALES);
+ configOut.mLocaleList = LocaleList.forLanguageTags(localesStr);
+ configOut.locale = configOut.mLocaleList.getPrimary();
configOut.touchscreen = XmlUtils.readIntAttribute(parser, XML_ATTR_TOUCHSCREEN,
TOUCHSCREEN_UNDEFINED);
@@ -1807,8 +1888,9 @@
if (config.mnc != 0) {
XmlUtils.writeIntAttribute(xml, XML_ATTR_MNC, config.mnc);
}
- if (config.locale != null) {
- XmlUtils.writeStringAttribute(xml, XML_ATTR_LOCALE, config.locale.toLanguageTag());
+ config.fixUpLocaleList();
+ if (!config.mLocaleList.isEmpty()) {
+ XmlUtils.writeStringAttribute(xml, XML_ATTR_LOCALES, config.mLocaleList.toLanguageTags());
}
if (config.touchscreen != TOUCHSCREEN_UNDEFINED) {
XmlUtils.writeIntAttribute(xml, XML_ATTR_TOUCHSCREEN, config.touchscreen);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 7cff11b..04caa8f 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -27,6 +27,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.keystore.AndroidKeyStoreProvider;
@@ -392,6 +393,18 @@
};
/**
+ * @hide
+ */
+ public static abstract class LockoutResetCallback {
+
+ /**
+ * Called when lockout period expired and clients are allowed to listen for fingerprint
+ * again.
+ */
+ public void onLockoutReset() { }
+ };
+
+ /**
* Request authentication of a crypto object. This call warms up the fingerprint hardware
* and starts scanning for a fingerprint. It terminates when
* {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
@@ -680,10 +693,45 @@
try {
mService.resetTimeout(token);
} catch (RemoteException e) {
- Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e);
+ Log.v(TAG, "Remote exception in resetTimeout(): ", e);
}
} else {
- Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+ Log.w(TAG, "resetTimeout(): Service not connected!");
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void addLockoutResetCallback(final LockoutResetCallback callback) {
+ if (mService != null) {
+ try {
+ final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+ mService.addLockoutResetCallback(
+ new IFingerprintServiceLockoutResetCallback.Stub() {
+
+ @Override
+ public void onLockoutReset(long deviceId) throws RemoteException {
+ final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, "lockoutResetCallback");
+ wakeLock.acquire();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ callback.onLockoutReset();
+ } finally {
+ wakeLock.release();
+ }
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception in addLockoutResetCallback(): ", e);
+ }
+ } else {
+ Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
}
}
@@ -724,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);
@@ -914,4 +962,3 @@
};
}
-
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 3356354..690a751 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,6 +17,7 @@
import android.os.Bundle;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
import android.hardware.fingerprint.Fingerprint;
import java.util.List;
@@ -71,4 +72,7 @@
// Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
void resetTimeout(in byte [] cryptoToken);
+
+ // Add a callback which gets notified when the fingerprint lockout period expired.
+ void addLockoutResetCallback(IFingerprintServiceLockoutResetCallback callback);
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl
new file mode 100644
index 0000000..e027a2b3
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl
@@ -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 android.hardware.fingerprint;
+
+import android.hardware.fingerprint.Fingerprint;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Callback when lockout period expired and clients are allowed to authenticate again.
+ * @hide
+ */
+interface IFingerprintServiceLockoutResetCallback {
+
+ /** Method is synchronous so wakelock is held when this is called from a WAKEUP alarm. */
+ void onLockoutReset(long deviceId);
+}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 465d142..c8b45c7 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -19,6 +19,7 @@
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.IInputDevicesChangedListener;
+import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.TouchCalibration;
import android.os.IBinder;
import android.view.InputDevice;
@@ -60,6 +61,9 @@
// Registers an input devices changed listener.
void registerInputDevicesChangedListener(IInputDevicesChangedListener listener);
+ // Registers a tablet mode change listener
+ void registerTabletModeChangedListener(ITabletModeChangedListener listener);
+
// Input device vibrator control.
void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
void cancelVibrate(int deviceId, IBinder token);
diff --git a/tools/aidl/os.h b/core/java/android/hardware/input/ITabletModeChangedListener.aidl
similarity index 60%
copy from tools/aidl/os.h
copy to core/java/android/hardware/input/ITabletModeChangedListener.aidl
index 79d2c35..a8559a7 100644
--- a/tools/aidl/os.h
+++ b/core/java/android/hardware/input/ITabletModeChangedListener.aidl
@@ -1,11 +1,11 @@
/*
- * 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,
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-#ifndef _FRAMEWORKS_BASE_TOOLS_AIDL_OS_SEP_H_
-#define _FRAMEWORKS_BASE_TOOLS_AIDL_OS_SEP_H_
+package android.hardware.input;
-#if defined(_WIN32)
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
-
-#endif
+/** @hide */
+interface ITabletModeChangedListener {
+ /* Called when the device enters or exits tablet mode. */
+ oneway void onTabletModeChanged(long whenNanos, boolean inTabletMode);
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 444f020..bae5757 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,7 @@
package android.hardware.input;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import android.annotation.SdkConstant;
@@ -29,6 +30,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -38,6 +40,7 @@
import android.view.InputEvent;
import java.util.ArrayList;
+import java.util.List;
/**
* Provides information about input devices and available key layouts.
@@ -67,6 +70,11 @@
private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners =
new ArrayList<InputDeviceListenerDelegate>();
+ // Guarded by mTabletModeLock
+ private final Object mTabletModeLock = new Object();
+ private TabletModeChangedListener mTabletModeChangedListener;
+ private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners;
+
/**
* Broadcast Action: Query available keyboard layouts.
* <p>
@@ -291,6 +299,7 @@
}
synchronized (mInputDevicesLock) {
+ populateInputDevicesLocked();
int index = findInputDeviceListenerLocked(listener);
if (index < 0) {
mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler));
@@ -331,6 +340,72 @@
}
/**
+ * Register a tablet mode changed listener.
+ *
+ * @param listener The listener to register.
+ * @param handler The handler on which the listener should be invoked, or null
+ * if the listener should be invoked on the calling thread's looper.
+ * @hide
+ */
+ public void registerOnTabletModeChangedListener(
+ OnTabletModeChangedListener listener, Handler handler) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+ synchronized (mTabletModeLock) {
+ if (mOnTabletModeChangedListeners == null) {
+ initializeTabletModeListenerLocked();
+ }
+ int idx = findOnTabletModeChangedListenerLocked(listener);
+ if (idx < 0) {
+ OnTabletModeChangedListenerDelegate d =
+ new OnTabletModeChangedListenerDelegate(listener, handler);
+ mOnTabletModeChangedListeners.add(d);
+ }
+ }
+ }
+
+ /**
+ * Unregister a tablet mode changed listener.
+ *
+ * @param listener The listener to unregister.
+ * @hide
+ */
+ public void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+ synchronized (mTabletModeLock) {
+ int idx = findOnTabletModeChangedListenerLocked(listener);
+ if (idx >= 0) {
+ OnTabletModeChangedListenerDelegate d = mOnTabletModeChangedListeners.remove(idx);
+ d.removeCallbacksAndMessages(null);
+ }
+ }
+ }
+
+ private void initializeTabletModeListenerLocked() {
+ final TabletModeChangedListener listener = new TabletModeChangedListener();
+ try {
+ mIm.registerTabletModeChangedListener(listener);
+ } catch (RemoteException ex) {
+ throw new RuntimeException("Could not register tablet mode changed listener", ex);
+ }
+ mTabletModeChangedListener = listener;
+ mOnTabletModeChangedListeners = new ArrayList<>();
+ }
+
+ private int findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener) {
+ final int N = mOnTabletModeChangedListeners.size();
+ for (int i = 0; i < N; i++) {
+ if (mOnTabletModeChangedListeners.get(i).mListener == listener) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
* Gets information about all supported keyboard layouts.
* <p>
* The input manager consults the built-in keyboard layouts as well
@@ -769,6 +844,22 @@
return false;
}
+
+ private void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
+ if (DEBUG) {
+ Log.d(TAG, "Received tablet mode changed: "
+ + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode);
+ }
+ synchronized (mTabletModeLock) {
+ final int N = mOnTabletModeChangedListeners.size();
+ for (int i = 0; i < N; i++) {
+ OnTabletModeChangedListenerDelegate listener =
+ mOnTabletModeChangedListeners.get(i);
+ listener.sendTabletModeChanged(whenNanos, inTabletMode);
+ }
+ }
+ }
+
/**
* Gets a vibrator service associated with an input device, assuming it has one.
* @return The vibrator, never null.
@@ -838,6 +929,57 @@
}
}
+ /** @hide */
+ public interface OnTabletModeChangedListener {
+ /**
+ * Called whenever the device goes into or comes out of tablet mode.
+ *
+ * @param whenNanos The time at which the device transitioned into or
+ * out of tablet mode. This is given in nanoseconds in the
+ * {@link SystemClock#uptimeMillis} time base.
+ */
+ void onTabletModeChanged(long whenNanos, boolean inTabletMode);
+ }
+
+ private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub {
+ @Override
+ public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
+ InputManager.this.onTabletModeChanged(whenNanos, inTabletMode);
+ }
+ }
+
+ private static final class OnTabletModeChangedListenerDelegate extends Handler {
+ private static final int MSG_TABLET_MODE_CHANGED = 0;
+
+ public final OnTabletModeChangedListener mListener;
+
+ public OnTabletModeChangedListenerDelegate(
+ OnTabletModeChangedListener listener, Handler handler) {
+ super(handler != null ? handler.getLooper() : Looper.myLooper());
+ mListener = listener;
+ }
+
+ public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
+ args.argi2 = (int) (whenNanos >> 32);
+ args.arg1 = (Boolean) inTabletMode;
+ obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TABLET_MODE_CHANGED:
+ SomeArgs args = (SomeArgs) msg.obj;
+ long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
+ boolean inTabletMode = (boolean) args.arg1;
+ mListener.onTabletModeChanged(whenNanos, inTabletMode);
+ break;
+ }
+ }
+ }
+
private final class InputDeviceVibrator extends Vibrator {
private final int mDeviceId;
private final Binder mToken;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 112ae10..6e28982 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1456,15 +1456,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 +1979,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 +2113,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 ec0cc6d..9a2a241 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -100,6 +100,16 @@
public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
/**
+ * A temporary hack until SUPL system can get off the legacy APIS.
+ * They do too many network requests and the long list of apps listening
+ * and waking due to the CONNECTIVITY_ACTION bcast makes it expensive.
+ * Use this bcast intent instead for SUPL requests.
+ * @hide
+ */
+ public static final String CONNECTIVITY_ACTION_SUPL =
+ "android.net.conn.CONNECTIVITY_CHANGE_SUPL";
+
+ /**
* The device has connected to a network that has presented a captive
* portal, which is blocking Internet connectivity. The user was presented
* with a notification that network sign in is required,
@@ -1233,6 +1243,8 @@
/** The hardware does not support this request. */
public static final int ERROR_HARDWARE_UNSUPPORTED = -30;
+ /** The hardware returned an error. */
+ public static final int ERROR_HARDWARE_ERROR = -31;
public static final int NATT_PORT = 4500;
@@ -2249,6 +2261,7 @@
private final AtomicInteger mRefCount;
private static final String TAG = "ConnectivityManager.CallbackHandler";
private final ConnectivityManager mCm;
+ private static final boolean DBG = false;
CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallback>callbackMap,
AtomicInteger refCount, ConnectivityManager cm) {
@@ -2260,7 +2273,7 @@
@Override
public void handleMessage(Message message) {
- Log.d(TAG, "CM callback handler got msg " + message.what);
+ if (DBG) Log.d(TAG, "CM callback handler got msg " + message.what);
NetworkRequest request = (NetworkRequest) getObject(message, NetworkRequest.class);
Network network = (Network) getObject(message, Network.class);
switch (message.what) {
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 5f46c73..cab88b9 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -169,7 +169,8 @@
}
}
- private void handleAddRequest(NetworkRequest request, int score) {
+ @VisibleForTesting
+ protected void handleAddRequest(NetworkRequest request, int score) {
NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
if (n == null) {
if (DBG) log("got request " + request + " with score " + score);
@@ -184,7 +185,8 @@
evalRequest(n);
}
- private void handleRemoveRequest(NetworkRequest request) {
+ @VisibleForTesting
+ protected void handleRemoveRequest(NetworkRequest request) {
NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
if (n != null) {
mNetworkRequests.remove(request.requestId);
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 4ad9d6d..bad94fc 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -468,6 +468,7 @@
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
* @see BatteryStats#getCpuSpeedSteps()
*/
+ @Deprecated
public abstract long getTimeAtCpuSpeed(int step, int which);
public static abstract class Sensor {
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 e742f98..70cff00 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -108,6 +108,12 @@
public abstract void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis);
/**
+ * Used by the window manager to tell the power manager that the user is no longer actively
+ * using the device.
+ */
+ public abstract void setUserInactiveOverrideFromWindowManager();
+
+ /**
* Used by device administration to set the maximum screen off timeout.
*
* This method must only be called by the device administration policy manager.
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 4da88ee..7529c52 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -77,6 +77,8 @@
public static final long TRACE_TAG_POWER = 1L << 17;
/** @hide */
public static final long TRACE_TAG_PACKAGE_MANAGER = 1L << 18;
+ /** @hide */
+ public static final long TRACE_TAG_SYSTEM_SERVER = 1L << 19;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 48ede4f..213e083 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -57,15 +57,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);
@@ -90,7 +90,7 @@
* 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 +102,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 +130,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,12 +139,12 @@
}
/** @hide */
- public static final int getCallingUserId() {
+ public static int getCallingUserId() {
return getUserId(Binder.getCallingUid());
}
/** @hide */
- public static final UserHandle getCallingUserHandle() {
+ public static UserHandle getCallingUserHandle() {
int userId = getUserId(Binder.getCallingUid());
UserHandle userHandle = userHandles.get(userId);
// Intentionally not synchronized to save time
@@ -159,7 +159,7 @@
* 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 +171,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 +179,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 +187,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 +196,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 +272,7 @@
* @hide
*/
@SystemApi
- public static final int myUserId() {
+ public static int myUserId() {
return getUserId(Process.myUid());
}
@@ -280,9 +280,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/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/LocaleList.java b/core/java/android/util/LocaleList.java
index 017735a..379651e 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -16,15 +16,19 @@
package android.util;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.Size;
+
+import com.android.internal.annotations.GuardedBy;
import java.util.HashSet;
import java.util.Locale;
// TODO: We don't except too many LocaleLists to exist at the same time, and
// we need access to the data at native level, so we should pass the data
-// down to the native level, create a mapt of every list seen there, take a
-// pointer back, and just keep that pointed in the Java-level object, so
+// down to the native level, create a map of every list seen there, take a
+// pointer back, and just keep that pointer in the Java-level object, so
// things could be copied very quickly.
/**
@@ -34,6 +38,7 @@
public final class LocaleList {
private final Locale[] mList;
private static final Locale[] sEmptyList = new Locale[0];
+ private static final LocaleList sEmptyLocaleList = new LocaleList();
public Locale get(int location) {
return location < mList.length ? mList[location] : null;
@@ -51,6 +56,60 @@
return mList.length;
}
+ @Override
+ public boolean equals(Object other) {
+ if (other == this)
+ return true;
+ if (!(other instanceof LocaleList))
+ return false;
+ final Locale[] otherList = ((LocaleList) other).mList;
+ if (mList.length != otherList.length)
+ return false;
+ for (int i = 0; i < mList.length; ++i) {
+ if (!mList[i].equals(otherList[i]))
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ for (int i = 0; i < mList.length; ++i) {
+ result = 31 * result + mList[i].hashCode();
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ for (int i = 0; i < mList.length; ++i) {
+ sb.append(mList[i]);
+ if (i < mList.length - 1) {
+ sb.append(',');
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public String toLanguageTags() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < mList.length; ++i) {
+ sb.append(mList[i].toLanguageTag());
+ if (i < mList.length - 1) {
+ sb.append(',');
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * It is almost always better to call {@link #getEmptyLocaleList()} instead which returns
+ * a pre-constructed empty locale list.
+ */
public LocaleList() {
mList = sEmptyList;
}
@@ -59,6 +118,19 @@
* @throws NullPointerException if any of the input locales is <code>null</code>.
* @throws IllegalArgumentException if any of the input locales repeat.
*/
+ public LocaleList(@Nullable Locale locale) {
+ if (locale == null) {
+ mList = sEmptyList;
+ } else {
+ mList = new Locale[1];
+ mList[0] = (Locale) locale.clone();
+ }
+ }
+
+ /**
+ * @throws NullPointerException if any of the input locales is <code>null</code>.
+ * @throws IllegalArgumentException if any of the input locales repeat.
+ */
public LocaleList(@Nullable Locale[] list) {
if (list == null || list.length == 0) {
mList = sEmptyList;
@@ -79,4 +151,39 @@
mList = localeList;
}
}
+
+ public static LocaleList getEmptyLocaleList() {
+ return sEmptyLocaleList;
+ }
+
+ public static LocaleList forLanguageTags(@Nullable String list) {
+ if (list == null || list.equals("")) {
+ return getEmptyLocaleList();
+ } else {
+ final String[] tags = list.split(",");
+ final Locale[] localeArray = new Locale[tags.length];
+ for (int i = 0; i < localeArray.length; ++i) {
+ localeArray[i] = Locale.forLanguageTag(tags[i]);
+ }
+ return new LocaleList(localeArray);
+ }
+ }
+
+ private final static Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static LocaleList sDefaultLocaleList;
+
+ // TODO: fix this to return the default system locale list once we have that
+ @NonNull @Size(min=1)
+ public static LocaleList getDefault() {
+ Locale defaultLocale = Locale.getDefault();
+ synchronized (sLock) {
+ if (sDefaultLocaleList == null || sDefaultLocaleList.size() != 1
+ || !defaultLocale.equals(sDefaultLocaleList.getPrimary())) {
+ sDefaultLocaleList = new LocaleList(defaultLocale);
+ }
+ }
+ return sDefaultLocaleList;
+ }
}
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/HandlerActionQueue.java b/core/java/android/view/HandlerActionQueue.java
new file mode 100644
index 0000000..4758a34
--- /dev/null
+++ b/core/java/android/view/HandlerActionQueue.java
@@ -0,0 +1,127 @@
+/*
+ * 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 com.android.internal.util.GrowingArrayUtils;
+
+import android.os.Handler;
+
+import java.util.ArrayList;
+
+/**
+ * Class used to enqueue pending work from Views when no Handler is attached.
+ *
+ * @hide Exposed for test framework only.
+ */
+public class HandlerActionQueue {
+ private HandlerAction[] mActions;
+ private int mCount;
+
+ public void post(Runnable action) {
+ postDelayed(action, 0);
+ }
+
+ public void postDelayed(Runnable action, long delayMillis) {
+ final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
+
+ synchronized (this) {
+ if (mActions == null) {
+ mActions = new HandlerAction[4];
+ }
+ mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
+ mCount++;
+ }
+ }
+
+ public void removeCallbacks(Runnable action) {
+ synchronized (this) {
+ final int count = mCount;
+ int j = 0;
+
+ final HandlerAction[] actions = mActions;
+ for (int i = 0; i < count; i++) {
+ if (actions[i].matches(action)) {
+ // Remove this action by overwriting it within
+ // this loop or nulling it out later.
+ continue;
+ }
+
+ if (j != i) {
+ // At least one previous entry was removed, so
+ // this one needs to move to the "new" list.
+ actions[j] = actions[i];
+ }
+
+ j++;
+ }
+
+ // The "new" list only has j entries.
+ mCount = j;
+
+ // Null out any remaining entries.
+ for (; j < count; j++) {
+ actions[j] = null;
+ }
+ }
+ }
+
+ public void executeActions(Handler handler) {
+ synchronized (this) {
+ final HandlerAction[] actions = mActions;
+ for (int i = 0, count = mCount; i < count; i++) {
+ final HandlerAction handlerAction = actions[i];
+ handler.postDelayed(handlerAction.action, handlerAction.delay);
+ }
+
+ mActions = null;
+ mCount = 0;
+ }
+ }
+
+ public int size() {
+ return mCount;
+ }
+
+ public Runnable getRunnable(int index) {
+ if (index >= mCount) {
+ throw new IndexOutOfBoundsException();
+ }
+ return mActions[index].action;
+ }
+
+ public long getDelay(int index) {
+ if (index >= mCount) {
+ throw new IndexOutOfBoundsException();
+ }
+ return mActions[index].delay;
+ }
+
+ private static class HandlerAction {
+ final Runnable action;
+ final long delay;
+
+ public HandlerAction(Runnable action, long delay) {
+ this.action = action;
+ this.delay = delay;
+ }
+
+ public boolean matches(Runnable otherAction) {
+ return otherAction == null && action == null
+ || action != null && action.equals(otherAction);
+ }
+ }
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f86adfe..32ef995 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -97,21 +97,22 @@
* @param launchTaskBehind True if the token is been launched from behind.
* @param taskBounds Bounds to use when creating a new Task with the input task Id if
* the task doesn't exist yet.
- * @return The configuration of the task if it was newly created. null otherwise.
+ * @param configuration Configuration that is being used with this task.
+ * @param cropWindowsToStack True if the app windows should be cropped to the stack bounds.
*/
- Configuration addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
+ 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 Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack);
/**
*
* @param token The token we are adding to the input task Id.
* @param taskId The Id of the task we are adding the token to.
* @param taskBounds Bounds to use when creating a new Task with the input task Id if
* the task doesn't exist yet.
- * @return The configuration of the task if it was newly created. null otherwise.
+ * @param config Configuration that is being used with this task.
*/
- Configuration setAppTask(IBinder token, int taskId, in Rect taskBounds);
+ void setAppTask(IBinder token, int taskId, in Rect taskBounds, in Configuration config);
void setAppOrientation(IApplicationToken token, int requestedOrientation);
int getAppOrientation(IApplicationToken token);
void setFocusedApp(IBinder token, boolean moveFocusNow);
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/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 6d89824..1c20cab 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -788,8 +788,10 @@
/** Key code constant: Step backward media key.
* Steps media backward, one frame at a time. */
public static final int KEYCODE_MEDIA_STEP_BACKWARD = 275;
+ /** Key code constant: put device to sleep unless a wakelock is held. */
+ public static final int KEYCODE_SOFT_SLEEP = 276;
- private static final int LAST_KEYCODE = KEYCODE_MEDIA_STEP_BACKWARD;
+ private static final int LAST_KEYCODE = KEYCODE_SOFT_SLEEP;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5970c3f..bcf9b2c 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -484,6 +484,7 @@
public boolean secure;
public long appVsyncOffsetNanos;
public long presentationDeadlineNanos;
+ public int colorTransform;
public PhysicalDisplayInfo() {
}
@@ -507,7 +508,8 @@
&& yDpi == other.yDpi
&& secure == other.secure
&& appVsyncOffsetNanos == other.appVsyncOffsetNanos
- && presentationDeadlineNanos == other.presentationDeadlineNanos;
+ && presentationDeadlineNanos == other.presentationDeadlineNanos
+ && colorTransform == other.colorTransform;
}
@Override
@@ -525,6 +527,7 @@
secure = other.secure;
appVsyncOffsetNanos = other.appVsyncOffsetNanos;
presentationDeadlineNanos = other.presentationDeadlineNanos;
+ colorTransform = other.colorTransform;
}
// For debugging purposes
@@ -533,7 +536,8 @@
return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
+ "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure
+ ", appVsyncOffset " + appVsyncOffsetNanos
- + ", bufferDeadline " + presentationDeadlineNanos + "}";
+ + ", bufferDeadline " + presentationDeadlineNanos
+ + ", colorTransform " + colorTransform + "}";
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 16d9bf3..eb591c1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3823,6 +3823,12 @@
private static SparseArray<String> mAttributeMap;
/**
+ * Queue of pending runnables. Used to postpone calls to post() until this
+ * view is attached and has a handler.
+ */
+ private HandlerActionQueue mRunQueue;
+
+ /**
* @hide
*/
String mStartActivityRequestWho;
@@ -6681,14 +6687,15 @@
}
/**
- * Gets the {@link View} description. It briefly describes the view and is
- * primarily used for accessibility support. Set this property to enable
- * better accessibility support for your application. This is especially
- * true for views that do not have textual representation (For example,
- * ImageButton).
+ * Returns the {@link View}'s content description.
+ * <p>
+ * <strong>Note:</strong> Do not override this method, as it will have no
+ * effect on the content description presented to accessibility services.
+ * You must call {@link #setContentDescription(CharSequence)} to modify the
+ * content description.
*
- * @return The content description.
- *
+ * @return the content description
+ * @see #setContentDescription(CharSequence)
* @attr ref android.R.styleable#View_contentDescription
*/
@ViewDebug.ExportedProperty(category = "accessibility")
@@ -6697,14 +6704,19 @@
}
/**
- * Sets the {@link View} description. It briefly describes the view and is
- * primarily used for accessibility support. Set this property to enable
- * better accessibility support for your application. This is especially
- * true for views that do not have textual representation (For example,
- * ImageButton).
+ * Sets the {@link View}'s content description.
+ * <p>
+ * A content description briefly describes the view and is primarily used
+ * for accessibility support to determine how a view should be presented to
+ * the user. In the case of a view with no textual representation, such as
+ * {@link android.widget.ImageButton}, a useful content description
+ * explains what the view does. For example, an image button with a phone
+ * icon that is used to place a call may use "Call" as its content
+ * description. An image of a floppy disk that is used to save a file may
+ * use "Save".
*
* @param contentDescription The content description.
- *
+ * @see #getContentDescription()
* @attr ref android.R.styleable#View_contentDescription
*/
@RemotableViewMethod
@@ -13010,6 +13022,18 @@
}
/**
+ * Returns the queue of runnable for this view.
+ *
+ * @return the queue of runnables for this view
+ */
+ private HandlerActionQueue getRunQueue() {
+ if (mRunQueue == null) {
+ mRunQueue = new HandlerActionQueue();
+ }
+ return mRunQueue;
+ }
+
+ /**
* Gets the view root associated with the View.
* @return The view root, or null if none.
* @hide
@@ -13046,8 +13070,10 @@
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
- // Assume that post will succeed later
- ViewRootImpl.getRunQueue().post(action);
+
+ // Postpone the runnable until we know on which thread it needs to run.
+ // Assume that the runnable will be successfully placed after attach.
+ getRunQueue().post(action);
return true;
}
@@ -13075,8 +13101,10 @@
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
- // Assume that post will succeed later
- ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
+
+ // Postpone the runnable until we know on which thread it needs to run.
+ // Assume that the runnable will be successfully placed after attach.
+ getRunQueue().postDelayed(action, delayMillis);
return true;
}
@@ -13095,8 +13123,9 @@
attachInfo.mViewRootImpl.mChoreographer.postCallback(
Choreographer.CALLBACK_ANIMATION, action, null);
} else {
- // Assume that post will succeed later
- ViewRootImpl.getRunQueue().post(action);
+ // Postpone the runnable until we know
+ // on which thread it needs to run.
+ getRunQueue().post(action);
}
}
@@ -13118,8 +13147,9 @@
attachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
Choreographer.CALLBACK_ANIMATION, action, null, delayMillis);
} else {
- // Assume that post will succeed later
- ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
+ // Postpone the runnable until we know
+ // on which thread it needs to run.
+ getRunQueue().postDelayed(action, delayMillis);
}
}
@@ -13146,8 +13176,7 @@
attachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
Choreographer.CALLBACK_ANIMATION, action, null);
}
- // Assume that post will succeed later
- ViewRootImpl.getRunQueue().removeCallbacks(action);
+ getRunQueue().removeCallbacks(action);
}
return true;
}
@@ -14565,7 +14594,6 @@
* this view
*/
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
- //System.out.println("Attached! " + this);
mAttachInfo = info;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
@@ -14581,6 +14609,11 @@
mAttachInfo.mScrollContainers.add(this);
mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;
}
+ // Transfer all pending runnables.
+ if (mRunQueue != null) {
+ mRunQueue.executeActions(info.mHandler);
+ mRunQueue = null;
+ }
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
@@ -16440,7 +16473,7 @@
*/
void setBackgroundBounds() {
if (mBackgroundSizeChanged && mBackground != null) {
- mBackground.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
+ mBackground.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
rebuildOutline();
}
@@ -16866,7 +16899,9 @@
Choreographer.CALLBACK_ANIMATION, what, who,
Choreographer.subtractFrameDelay(delay));
} else {
- ViewRootImpl.getRunQueue().postDelayed(what, delay);
+ // Postpone the runnable until we know
+ // on which thread it needs to run.
+ getRunQueue().postDelayed(what, delay);
}
}
}
@@ -16884,7 +16919,7 @@
mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
Choreographer.CALLBACK_ANIMATION, what, who);
}
- ViewRootImpl.getRunQueue().removeCallbacks(what);
+ getRunQueue().removeCallbacks(what);
}
}
@@ -17007,27 +17042,33 @@
@CallSuper
protected void drawableStateChanged() {
final int[] state = getDrawableState();
+ boolean changed = false;
final Drawable bg = mBackground;
if (bg != null && bg.isStateful()) {
- bg.setState(state);
+ changed |= bg.setState(state);
}
final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
if (fg != null && fg.isStateful()) {
- fg.setState(state);
+ changed |= fg.setState(state);
}
if (mScrollCache != null) {
final Drawable scrollBar = mScrollCache.scrollBar;
if (scrollBar != null && scrollBar.isStateful()) {
- scrollBar.setState(state);
+ changed |= scrollBar.setState(state)
+ && mScrollCache.state != ScrollabilityCache.OFF;
}
}
if (mStateListAnimator != null) {
mStateListAnimator.setState(state);
}
+
+ if (changed) {
+ invalidate();
+ }
}
/**
@@ -19709,6 +19750,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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8bbaf36..84ba450 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -28,7 +28,6 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
@@ -131,7 +130,7 @@
*/
static final int MAX_TRACKBALL_DELAY = 250;
- static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
+ static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
static boolean sFirstDrawComplete = false;
@@ -577,21 +576,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);
@@ -6759,90 +6757,17 @@
}
}
- static RunQueue getRunQueue() {
- RunQueue rq = sRunQueues.get();
+ static HandlerActionQueue getRunQueue() {
+ HandlerActionQueue rq = sRunQueues.get();
if (rq != null) {
return rq;
}
- rq = new RunQueue();
+ rq = new HandlerActionQueue();
sRunQueues.set(rq);
return rq;
}
/**
- * The run queue is used to enqueue pending work from Views when no Handler is
- * attached. The work is executed during the next call to performTraversals on
- * the thread.
- * @hide
- */
- static final class RunQueue {
- private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
-
- void post(Runnable action) {
- postDelayed(action, 0);
- }
-
- void postDelayed(Runnable action, long delayMillis) {
- HandlerAction handlerAction = new HandlerAction();
- handlerAction.action = action;
- handlerAction.delay = delayMillis;
-
- synchronized (mActions) {
- mActions.add(handlerAction);
- }
- }
-
- void removeCallbacks(Runnable action) {
- final HandlerAction handlerAction = new HandlerAction();
- handlerAction.action = action;
-
- synchronized (mActions) {
- final ArrayList<HandlerAction> actions = mActions;
-
- while (actions.remove(handlerAction)) {
- // Keep going
- }
- }
- }
-
- void executeActions(Handler handler) {
- synchronized (mActions) {
- final ArrayList<HandlerAction> actions = mActions;
- final int count = actions.size();
-
- for (int i = 0; i < count; i++) {
- final HandlerAction handlerAction = actions.get(i);
- handler.postDelayed(handlerAction.action, handlerAction.delay);
- }
-
- actions.clear();
- }
- }
-
- private static class HandlerAction {
- Runnable action;
- long delay;
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- HandlerAction that = (HandlerAction) o;
- return !(action != null ? !action.equals(that.action) : that.action != null);
-
- }
-
- @Override
- public int hashCode() {
- int result = action != null ? action.hashCode() : 0;
- result = 31 * result + (int) (delay ^ (delay >>> 32));
- return result;
- }
- }
- }
-
- /**
* Class for managing the accessibility interaction connection
* based on the global accessibility state.
*/
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 7a3d882..0e7089f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -58,14 +58,28 @@
/** Flag for the "no title" feature, turning off the title at the top
* of the screen. */
public static final int FEATURE_NO_TITLE = 1;
- /** Flag for the progress indicator feature */
+
+ /**
+ * Flag for the progress indicator feature.
+ *
+ * @deprecated No longer supported starting in API 21.
+ */
+ @Deprecated
public static final int FEATURE_PROGRESS = 2;
+
/** Flag for having an icon on the left side of the title bar */
public static final int FEATURE_LEFT_ICON = 3;
/** Flag for having an icon on the right side of the title bar */
public static final int FEATURE_RIGHT_ICON = 4;
- /** Flag for indeterminate progress */
+
+ /**
+ * Flag for indeterminate progress.
+ *
+ * @deprecated No longer supported starting in API 21.
+ */
+ @Deprecated
public static final int FEATURE_INDETERMINATE_PROGRESS = 5;
+
/** Flag for the context menu. This is enabled by default. */
public static final int FEATURE_CONTEXT_MENU = 6;
/** Flag for custom title. You cannot combine this feature with other title features. */
@@ -133,21 +147,76 @@
*/
public static final int FEATURE_MAX = FEATURE_ACTIVITY_TRANSITIONS;
- /** Flag for setting the progress bar's visibility to VISIBLE */
+ /**
+ * Flag for setting the progress bar's visibility to VISIBLE.
+ *
+ * @deprecated {@link #FEATURE_PROGRESS} and related methods are no longer
+ * supported starting in API 21.
+ */
+ @Deprecated
public static final int PROGRESS_VISIBILITY_ON = -1;
- /** Flag for setting the progress bar's visibility to GONE */
+
+ /**
+ * Flag for setting the progress bar's visibility to GONE.
+ *
+ * @deprecated {@link #FEATURE_PROGRESS} and related methods are no longer
+ * supported starting in API 21.
+ */
+ @Deprecated
public static final int PROGRESS_VISIBILITY_OFF = -2;
- /** Flag for setting the progress bar's indeterminate mode on */
+
+ /**
+ * Flag for setting the progress bar's indeterminate mode on.
+ *
+ * @deprecated {@link #FEATURE_INDETERMINATE_PROGRESS} and related methods
+ * are no longer supported starting in API 21.
+ */
+ @Deprecated
public static final int PROGRESS_INDETERMINATE_ON = -3;
- /** Flag for setting the progress bar's indeterminate mode off */
+
+ /**
+ * Flag for setting the progress bar's indeterminate mode off.
+ *
+ * @deprecated {@link #FEATURE_INDETERMINATE_PROGRESS} and related methods
+ * are no longer supported starting in API 21.
+ */
+ @Deprecated
public static final int PROGRESS_INDETERMINATE_OFF = -4;
- /** Starting value for the (primary) progress */
+
+ /**
+ * Starting value for the (primary) progress.
+ *
+ * @deprecated {@link #FEATURE_PROGRESS} and related methods are no longer
+ * supported starting in API 21.
+ */
+ @Deprecated
public static final int PROGRESS_START = 0;
- /** Ending value for the (primary) progress */
+
+ /**
+ * Ending value for the (primary) progress.
+ *
+ * @deprecated {@link #FEATURE_PROGRESS} and related methods are no longer
+ * supported starting in API 21.
+ */
+ @Deprecated
public static final int PROGRESS_END = 10000;
- /** Lowest possible value for the secondary progress */
+
+ /**
+ * Lowest possible value for the secondary progress.
+ *
+ * @deprecated {@link #FEATURE_PROGRESS} and related methods are no longer
+ * supported starting in API 21.
+ */
+ @Deprecated
public static final int PROGRESS_SECONDARY_START = 20000;
- /** Highest possible value for the secondary progress */
+
+ /**
+ * Highest possible value for the secondary progress.
+ *
+ * @deprecated {@link #FEATURE_PROGRESS} and related methods are no longer
+ * supported starting in API 21.
+ */
+ @Deprecated
public static final int PROGRESS_SECONDARY_END = 30000;
/**
@@ -493,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) {
@@ -1128,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.
@@ -1192,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.
*
@@ -1944,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/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e77b862..72971e8 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -199,10 +199,7 @@
} else {
userId = UserHandle.myUserId();
}
- IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
- IAccessibilityManager service = iBinder == null
- ? null : IAccessibilityManager.Stub.asInterface(iBinder);
- sInstance = new AccessibilityManager(context, service, userId);
+ sInstance = new AccessibilityManager(context, null, userId);
}
}
return sInstance;
@@ -219,10 +216,9 @@
*/
public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
mHandler = new MyHandler(context.getMainLooper());
- mService = service;
mUserId = userId;
synchronized (mLock) {
- tryConnectToServiceLocked();
+ tryConnectToServiceLocked(service);
}
}
@@ -612,17 +608,20 @@
private IAccessibilityManager getServiceLocked() {
if (mService == null) {
- tryConnectToServiceLocked();
+ tryConnectToServiceLocked(null);
}
return mService;
}
- private void tryConnectToServiceLocked() {
- IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
- if (iBinder == null) {
- return;
+ private void tryConnectToServiceLocked(IAccessibilityManager service) {
+ if (service == null) {
+ IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ if (iBinder == null) {
+ return;
+ }
+ service = IAccessibilityManager.Stub.asInterface(iBinder);
}
- IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
+
try {
final int stateFlags = service.addClient(mClient, mUserId);
setStateLocked(stateFlags);
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 586ae77..5724f52 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();
}
/**
@@ -2777,11 +2800,14 @@
}
void updateSelectorState() {
- if (mSelector != null) {
+ final Drawable selector = mSelector;
+ if (selector != null && selector.isStateful()) {
if (shouldShowSelector()) {
- mSelector.setState(getDrawableStateForSelector());
+ if (selector.setState(getDrawableStateForSelector())) {
+ invalidateDrawable(selector);
+ }
} else {
- mSelector.setState(StateSet.NOTHING);
+ selector.setState(StateSet.NOTHING);
}
}
}
@@ -6323,6 +6349,9 @@
*/
long itemId = -1;
+ /** Whether the adapter considers the item enabled. */
+ boolean isEnabled;
+
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
@@ -6348,6 +6377,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 76e91d1..6883db2 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -369,8 +369,9 @@
}
final Drawable thumb = mThumb;
- if (thumb != null && thumb.isStateful()) {
- thumb.setState(getDrawableState());
+ if (thumb != null && thumb.isStateful()
+ && thumb.setState(getDrawableState())) {
+ invalidateDrawable(thumb);
}
}
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index a5696ee..2dd84e3 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -37,7 +37,6 @@
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.ListPopupWindow.ForwardingListener;
import com.android.internal.view.ActionBarPolicy;
import com.android.internal.view.menu.ActionMenuItemView;
import com.android.internal.view.menu.BaseMenuPresenter;
@@ -45,6 +44,7 @@
import com.android.internal.view.menu.MenuItemImpl;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuView;
+import com.android.internal.view.menu.ShowableListMenu;
import com.android.internal.view.menu.SubMenuBuilder;
import java.util.ArrayList;
@@ -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;
}
@@ -828,7 +848,7 @@
setOnTouchListener(new ForwardingListener(this) {
@Override
- public ListPopupWindow getPopup() {
+ public ShowableListMenu getPopup() {
if (mOverflowPopup == null) {
return null;
}
@@ -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
@@ -1003,7 +1009,7 @@
private class ActionMenuPopupCallback extends ActionMenuItemView.PopupCallback {
@Override
- public ListPopupWindow getPopup() {
+ public ShowableListMenu getPopup() {
return mActionButtonPopup != null ? mActionButtonPopup.getPopup() : null;
}
}
diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java
index f34ad71..f5c46db 100644
--- a/core/java/android/widget/ActivityChooserView.java
+++ b/core/java/android/widget/ActivityChooserView.java
@@ -17,6 +17,7 @@
package android.widget;
import com.android.internal.R;
+import com.android.internal.view.menu.ShowableListMenu;
import android.annotation.StringRes;
import android.content.Context;
@@ -37,7 +38,6 @@
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ActivityChooserModel.ActivityChooserModelClient;
-import android.widget.ListPopupWindow.ForwardingListener;
/**
* This class is a view for choosing an activity for handling a given {@link Intent}.
@@ -263,7 +263,7 @@
});
expandButton.setOnTouchListener(new ForwardingListener(expandButton) {
@Override
- public ListPopupWindow getPopup() {
+ public ShowableListMenu getPopup() {
return getListPopupWindow();
}
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/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index c1d2756..a018d26 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -425,14 +425,11 @@
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
-
- if (mCheckMarkDrawable != null) {
- int[] myDrawableState = getDrawableState();
-
- // Set the state of the Drawable
- mCheckMarkDrawable.setState(myDrawableState);
-
- invalidate();
+
+ final Drawable checkMarkDrawable = mCheckMarkDrawable;
+ if (checkMarkDrawable != null && checkMarkDrawable.isStateful()
+ && checkMarkDrawable.setState(getDrawableState())) {
+ invalidateDrawable(checkMarkDrawable);
}
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 602e1ab..b19fe17 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -456,14 +456,11 @@
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
-
- if (mButtonDrawable != null) {
- int[] myDrawableState = getDrawableState();
-
- // Set the state of the Drawable
- mButtonDrawable.setState(myDrawableState);
-
- invalidate();
+
+ final Drawable buttonDrawable = mButtonDrawable;
+ if (buttonDrawable != null && buttonDrawable.isStateful()
+ && buttonDrawable.setState(getDrawableState())) {
+ invalidateDrawable(buttonDrawable);
}
}
diff --git a/core/java/android/widget/DropDownListView.java b/core/java/android/widget/DropDownListView.java
new file mode 100644
index 0000000..c869ccb
--- /dev/null
+++ b/core/java/android/widget/DropDownListView.java
@@ -0,0 +1,392 @@
+/*
+ * 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;
+
+
+import com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.IntProperty;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.TextView;
+import android.widget.ListView;
+
+
+/**
+ * Wrapper class for a ListView. This wrapper can hijack the focus to
+ * make sure the list uses the appropriate drawables and states when
+ * displayed on screen within a drop down. The focus is never actually
+ * passed to the drop down in this mode; the list only looks focused.
+ *
+ * @hide
+ */
+public class DropDownListView extends ListView {
+ /** Duration in milliseconds of the drag-to-open click animation. */
+ private static final long CLICK_ANIM_DURATION = 150;
+
+ /** Target alpha value for drag-to-open click animation. */
+ private static final int CLICK_ANIM_ALPHA = 0x80;
+
+ /** Wrapper around Drawable's <code>alpha</code> property. */
+ private static final IntProperty<Drawable> DRAWABLE_ALPHA =
+ new IntProperty<Drawable>("alpha") {
+ @Override
+ public void setValue(Drawable object, int value) {
+ object.setAlpha(value);
+ }
+
+ @Override
+ public Integer get(Drawable object) {
+ return object.getAlpha();
+ }
+ };
+
+ /*
+ * WARNING: This is a workaround for a touch mode issue.
+ *
+ * Touch mode is propagated lazily to windows. This causes problems in
+ * the following scenario:
+ * - Type something in the AutoCompleteTextView and get some results
+ * - Move down with the d-pad to select an item in the list
+ * - Move up with the d-pad until the selection disappears
+ * - Type more text in the AutoCompleteTextView *using the soft keyboard*
+ * and get new results; you are now in touch mode
+ * - The selection comes back on the first item in the list, even though
+ * the list is supposed to be in touch mode
+ *
+ * Using the soft keyboard triggers the touch mode change but that change
+ * is propagated to our window only after the first list layout, therefore
+ * after the list attempts to resurrect the selection.
+ *
+ * The trick to work around this issue is to pretend the list is in touch
+ * mode when we know that the selection should not appear, that is when
+ * we know the user moved the selection away from the list.
+ *
+ * This boolean is set to true whenever we explicitly hide the list's
+ * selection and reset to false whenever we know the user moved the
+ * selection back to the list.
+ *
+ * When this boolean is true, isInTouchMode() returns true, otherwise it
+ * returns super.isInTouchMode().
+ */
+ private boolean mListSelectionHidden;
+
+ /**
+ * True if this wrapper should fake focus.
+ */
+ private boolean mHijackFocus;
+
+ /** Whether to force drawing of the pressed state selector. */
+ private boolean mDrawsInPressedState;
+
+ /** Current drag-to-open click animation, if any. */
+ private Animator mClickAnimation;
+
+ /** Helper for drag-to-open auto scrolling. */
+ private AbsListViewAutoScroller mScrollHelper;
+
+ /**
+ * Creates a new list view wrapper.
+ *
+ * @param context this view's context
+ */
+ public DropDownListView(Context context, boolean hijackFocus) {
+ this(context, hijackFocus, com.android.internal.R.attr.dropDownListViewStyle);
+ }
+
+ /**
+ * Creates a new list view wrapper.
+ *
+ * @param context this view's context
+ */
+ public DropDownListView(Context context, boolean hijackFocus, int defStyleAttr) {
+ super(context, null, defStyleAttr);
+ mHijackFocus = hijackFocus;
+ // TODO: Add an API to control this
+ 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.
+ *
+ * @param activePointerId id of the pointer that activated forwarding
+ * @return whether the event was handled
+ */
+ public boolean onForwardedEvent(MotionEvent event, int activePointerId) {
+ boolean handledEvent = true;
+ boolean clearPressedItem = false;
+
+ final int actionMasked = event.getActionMasked();
+ switch (actionMasked) {
+ case MotionEvent.ACTION_CANCEL:
+ handledEvent = false;
+ break;
+ case MotionEvent.ACTION_UP:
+ handledEvent = false;
+ // $FALL-THROUGH$
+ case MotionEvent.ACTION_MOVE:
+ final int activeIndex = event.findPointerIndex(activePointerId);
+ if (activeIndex < 0) {
+ handledEvent = false;
+ break;
+ }
+
+ final int x = (int) event.getX(activeIndex);
+ final int y = (int) event.getY(activeIndex);
+ final int position = pointToPosition(x, y);
+ if (position == INVALID_POSITION) {
+ clearPressedItem = true;
+ break;
+ }
+
+ final View child = getChildAt(position - getFirstVisiblePosition());
+ setPressedItem(child, position, x, y);
+ handledEvent = true;
+
+ if (actionMasked == MotionEvent.ACTION_UP) {
+ clickPressedItem(child, position);
+ }
+ break;
+ }
+
+ // Failure to handle the event cancels forwarding.
+ if (!handledEvent || clearPressedItem) {
+ clearPressedItem();
+ }
+
+ // Manage automatic scrolling.
+ if (handledEvent) {
+ if (mScrollHelper == null) {
+ mScrollHelper = new AbsListViewAutoScroller(this);
+ }
+ mScrollHelper.setEnabled(true);
+ mScrollHelper.onTouch(this, event);
+ } else if (mScrollHelper != null) {
+ mScrollHelper.setEnabled(false);
+ }
+
+ return handledEvent;
+ }
+
+ /**
+ * Sets whether the list selection is hidden, as part of a workaround for a touch mode issue
+ * (see the declaration for mListSelectionHidden).
+ * @param listSelectionHidden
+ */
+ public void setListSelectionHidden(boolean listSelectionHidden) {
+ this.mListSelectionHidden = listSelectionHidden;
+ }
+
+ /**
+ * Starts an alpha animation on the selector. When the animation ends,
+ * the list performs a click on the item.
+ */
+ private void clickPressedItem(final View child, final int position) {
+ final long id = getItemIdAtPosition(position);
+ final Animator anim = ObjectAnimator.ofInt(
+ mSelector, DRAWABLE_ALPHA, 0xFF, CLICK_ANIM_ALPHA, 0xFF);
+ anim.setDuration(CLICK_ANIM_DURATION);
+ anim.setInterpolator(new AccelerateDecelerateInterpolator());
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ performItemClick(child, position, id);
+ }
+ });
+ anim.start();
+
+ if (mClickAnimation != null) {
+ mClickAnimation.cancel();
+ }
+ mClickAnimation = anim;
+ }
+
+ private void clearPressedItem() {
+ mDrawsInPressedState = false;
+ setPressed(false);
+ updateSelectorState();
+
+ final View motionView = getChildAt(mMotionPosition - mFirstPosition);
+ if (motionView != null) {
+ motionView.setPressed(false);
+ }
+
+ if (mClickAnimation != null) {
+ mClickAnimation.cancel();
+ mClickAnimation = null;
+ }
+ }
+
+ private void setPressedItem(View child, int position, float x, float y) {
+ mDrawsInPressedState = true;
+
+ // Ordering is essential. First, update the container's pressed state.
+ drawableHotspotChanged(x, y);
+ if (!isPressed()) {
+ setPressed(true);
+ }
+
+ // Next, run layout if we need to stabilize child positions.
+ if (mDataChanged) {
+ layoutChildren();
+ }
+
+ // Manage the pressed view based on motion position. This allows us to
+ // play nicely with actual touch and scroll events.
+ final View motionView = getChildAt(mMotionPosition - mFirstPosition);
+ if (motionView != null && motionView != child && motionView.isPressed()) {
+ motionView.setPressed(false);
+ }
+ mMotionPosition = position;
+
+ // Offset for child coordinates.
+ final float childX = x - child.getLeft();
+ final float childY = y - child.getTop();
+ child.drawableHotspotChanged(childX, childY);
+ if (!child.isPressed()) {
+ child.setPressed(true);
+ }
+
+ // Ensure that keyboard focus starts from the last touched position.
+ setSelectedPositionInt(position);
+ positionSelectorLikeTouch(position, child, x, y);
+
+ // Refresh the drawable state to reflect the new pressed state,
+ // which will also update the selector state.
+ refreshDrawableState();
+
+ if (mClickAnimation != null) {
+ mClickAnimation.cancel();
+ mClickAnimation = null;
+ }
+ }
+
+ @Override
+ boolean touchModeDrawsInPressedState() {
+ return mDrawsInPressedState || super.touchModeDrawsInPressedState();
+ }
+
+ /**
+ * Avoids jarring scrolling effect by ensuring that list elements
+ * made of a text view fit on a single line.
+ *
+ * @param position the item index in the list to get a view for
+ * @return the view for the specified item
+ */
+ @Override
+ View obtainView(int position, boolean[] isScrap) {
+ View view = super.obtainView(position, isScrap);
+
+ if (view instanceof TextView) {
+ ((TextView) view).setHorizontallyScrolling(true);
+ }
+
+ return view;
+ }
+
+ @Override
+ public boolean isInTouchMode() {
+ // WARNING: Please read the comment where mListSelectionHidden is declared
+ return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
+ }
+
+ /**
+ * Returns the focus state in the drop down.
+ *
+ * @return true always if hijacking focus
+ */
+ @Override
+ public boolean hasWindowFocus() {
+ return mHijackFocus || super.hasWindowFocus();
+ }
+
+ /**
+ * Returns the focus state in the drop down.
+ *
+ * @return true always if hijacking focus
+ */
+ @Override
+ public boolean isFocused() {
+ return mHijackFocus || super.isFocused();
+ }
+
+ /**
+ * Returns the focus state in the drop down.
+ *
+ * @return true always if hijacking focus
+ */
+ @Override
+ public boolean hasFocus() {
+ return mHijackFocus || super.hasFocus();
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/widget/ForwardingListener.java b/core/java/android/widget/ForwardingListener.java
new file mode 100644
index 0000000..7ddeff9
--- /dev/null
+++ b/core/java/android/widget/ForwardingListener.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 android.widget;
+
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
+
+import com.android.internal.view.menu.ShowableListMenu;
+
+/**
+ * Abstract class that forwards touch events to a {@link ShowableListMenu}.
+ *
+ * @hide
+ */
+public abstract class ForwardingListener
+ implements View.OnTouchListener, View.OnAttachStateChangeListener {
+
+ /** Scaled touch slop, used for detecting movement outside bounds. */
+ private final float mScaledTouchSlop;
+
+ /** Timeout before disallowing intercept on the source's parent. */
+ private final int mTapTimeout;
+
+ /** Timeout before accepting a long-press to start forwarding. */
+ private final int mLongPressTimeout;
+
+ /** Source view from which events are forwarded. */
+ private final View mSrc;
+
+ /** Runnable used to prevent conflicts with scrolling parents. */
+ private Runnable mDisallowIntercept;
+
+ /** Runnable used to trigger forwarding on long-press. */
+ private Runnable mTriggerLongPress;
+
+ /** Whether this listener is currently forwarding touch events. */
+ private boolean mForwarding;
+
+ /**
+ * Whether forwarding was initiated by a long-press. If so, we won't
+ * force the window to dismiss when the touch stream ends.
+ */
+ private boolean mWasLongPress;
+
+ /** The id of the first pointer down in the current event stream. */
+ private int mActivePointerId;
+
+ public ForwardingListener(View src) {
+ mSrc = src;
+ mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop();
+ mTapTimeout = ViewConfiguration.getTapTimeout();
+
+ // Use a medium-press timeout. Halfway between tap and long-press.
+ mLongPressTimeout = (mTapTimeout + ViewConfiguration.getLongPressTimeout()) / 2;
+
+ src.addOnAttachStateChangeListener(this);
+ }
+
+ /**
+ * Returns the popup to which this listener is forwarding events.
+ * <p>
+ * Override this to return the correct popup. If the popup is displayed
+ * asynchronously, you may also need to override
+ * {@link #onForwardingStopped} to prevent premature cancellation of
+ * forwarding.
+ *
+ * @return the popup to which this listener is forwarding events
+ */
+ public abstract ShowableListMenu getPopup();
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ final boolean wasForwarding = mForwarding;
+ final boolean forwarding;
+ if (wasForwarding) {
+ forwarding = onTouchForwarded(event) || !onForwardingStopped();
+ } else {
+ forwarding = onTouchObserved(event) && onForwardingStarted();
+
+ if (forwarding) {
+ // Make sure we cancel any ongoing source event stream.
+ final long now = SystemClock.uptimeMillis();
+ final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL,
+ 0.0f, 0.0f, 0);
+ mSrc.onTouchEvent(e);
+ e.recycle();
+ }
+ }
+
+ mForwarding = forwarding;
+ return forwarding || wasForwarding;
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ mForwarding = false;
+ mActivePointerId = MotionEvent.INVALID_POINTER_ID;
+
+ if (mDisallowIntercept != null) {
+ mSrc.removeCallbacks(mDisallowIntercept);
+ }
+ }
+
+ /**
+ * Called when forwarding would like to start.
+ * <p>
+ * By default, this will show the popup returned by {@link #getPopup()}.
+ * It may be overridden to perform another action, like clicking the
+ * source view or preparing the popup before showing it.
+ *
+ * @return true to start forwarding, false otherwise
+ */
+ protected boolean onForwardingStarted() {
+ final ShowableListMenu popup = getPopup();
+ if (popup != null && !popup.isShowing()) {
+ popup.show();
+ }
+ return true;
+ }
+
+ /**
+ * Called when forwarding would like to stop.
+ * <p>
+ * By default, this will dismiss the popup returned by
+ * {@link #getPopup()}. It may be overridden to perform some other
+ * action.
+ *
+ * @return true to stop forwarding, false otherwise
+ */
+ protected boolean onForwardingStopped() {
+ final ShowableListMenu popup = getPopup();
+ if (popup != null && popup.isShowing()) {
+ popup.dismiss();
+ }
+ return true;
+ }
+
+ /**
+ * Observes motion events and determines when to start forwarding.
+ *
+ * @param srcEvent motion event in source view coordinates
+ * @return true to start forwarding motion events, false otherwise
+ */
+ private boolean onTouchObserved(MotionEvent srcEvent) {
+ final View src = mSrc;
+ if (!src.isEnabled()) {
+ return false;
+ }
+
+ final int actionMasked = srcEvent.getActionMasked();
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = srcEvent.getPointerId(0);
+ mWasLongPress = false;
+
+ if (mDisallowIntercept == null) {
+ mDisallowIntercept = new DisallowIntercept();
+ }
+ src.postDelayed(mDisallowIntercept, mTapTimeout);
+
+ if (mTriggerLongPress == null) {
+ mTriggerLongPress = new TriggerLongPress();
+ }
+ src.postDelayed(mTriggerLongPress, mLongPressTimeout);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId);
+ if (activePointerIndex >= 0) {
+ final float x = srcEvent.getX(activePointerIndex);
+ final float y = srcEvent.getY(activePointerIndex);
+
+ // Has the pointer moved outside of the view?
+ if (!src.pointInView(x, y, mScaledTouchSlop)) {
+ clearCallbacks();
+
+ // Don't let the parent intercept our events.
+ src.getParent().requestDisallowInterceptTouchEvent(true);
+ return true;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ clearCallbacks();
+ break;
+ }
+
+ return false;
+ }
+
+ private void clearCallbacks() {
+ if (mTriggerLongPress != null) {
+ mSrc.removeCallbacks(mTriggerLongPress);
+ }
+
+ if (mDisallowIntercept != null) {
+ mSrc.removeCallbacks(mDisallowIntercept);
+ }
+ }
+
+ private void onLongPress() {
+ clearCallbacks();
+
+ final View src = mSrc;
+ if (!src.isEnabled() || src.isLongClickable()) {
+ // Ignore long-press if the view is disabled or has its own
+ // handler.
+ return;
+ }
+
+ if (!onForwardingStarted()) {
+ return;
+ }
+
+ // Don't let the parent intercept our events.
+ src.getParent().requestDisallowInterceptTouchEvent(true);
+
+ // Make sure we cancel any ongoing source event stream.
+ final long now = SystemClock.uptimeMillis();
+ final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
+ src.onTouchEvent(e);
+ e.recycle();
+
+ mForwarding = true;
+ mWasLongPress = true;
+ }
+
+ /**
+ * Handles forwarded motion events and determines when to stop
+ * forwarding.
+ *
+ * @param srcEvent motion event in source view coordinates
+ * @return true to continue forwarding motion events, false to cancel
+ */
+ private boolean onTouchForwarded(MotionEvent srcEvent) {
+ final View src = mSrc;
+ final ShowableListMenu popup = getPopup();
+ if (popup == null || !popup.isShowing()) {
+ return false;
+ }
+
+ final DropDownListView dst = (DropDownListView) popup.getListView();
+ if (dst == null || !dst.isShown()) {
+ return false;
+ }
+
+ // Convert event to destination-local coordinates.
+ final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent);
+ src.toGlobalMotionEvent(dstEvent);
+ dst.toLocalMotionEvent(dstEvent);
+
+ // Forward converted event to destination view, then recycle it.
+ final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId);
+ dstEvent.recycle();
+
+ // Always cancel forwarding when the touch stream ends.
+ final int action = srcEvent.getActionMasked();
+ final boolean keepForwarding = action != MotionEvent.ACTION_UP
+ && action != MotionEvent.ACTION_CANCEL;
+
+ return handled && keepForwarding;
+ }
+
+ private class DisallowIntercept implements Runnable {
+ @Override
+ public void run() {
+ final ViewParent parent = mSrc.getParent();
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ }
+
+ private class TriggerLongPress implements Runnable {
+ @Override
+ public void run() {
+ onLongPress();
+ }
+ }
+}
\ No newline at end of file
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/ImageView.java b/core/java/android/widget/ImageView.java
index b53af0c..245c7332 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -1165,9 +1165,10 @@
protected void drawableStateChanged() {
super.drawableStateChanged();
- final Drawable d = mDrawable;
- if (d != null && d.isStateful()) {
- d.setState(getDrawableState());
+ final Drawable drawable = mDrawable;
+ if (drawable != null && drawable.isStateful()
+ && drawable.setState(getDrawableState())) {
+ invalidateDrawable(drawable);
}
}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index a02efcf..b95bc28 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -25,7 +25,6 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.IntProperty;
@@ -36,13 +35,13 @@
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.View.OnTouchListener;
-import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import com.android.internal.R;
+import com.android.internal.view.menu.ShowableListMenu;
import com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller;
import java.util.Locale;
@@ -58,7 +57,7 @@
* @see android.widget.AutoCompleteTextView
* @see android.widget.Spinner
*/
-public class ListPopupWindow {
+public class ListPopupWindow implements ShowableListMenu {
private static final String TAG = "ListPopupWindow";
private static final boolean DEBUG = false;
@@ -70,7 +69,6 @@
private static final int EXPAND_LIST_TIMEOUT = 250;
private Context mContext;
- private PopupWindow mPopup;
private ListAdapter mAdapter;
private DropDownListView mDropDownList;
@@ -113,6 +111,8 @@
private int mLayoutDirection;
+ PopupWindow mPopup;
+
/**
* The provided prompt view should appear above list content.
*
@@ -580,6 +580,7 @@
* Show the popup list. If the list is already showing, this method
* will recalculate the popup's size and position.
*/
+ @Override
public void show() {
int height = buildDropDown();
@@ -671,6 +672,7 @@
/**
* Dismiss the popup window.
*/
+ @Override
public void dismiss() {
mPopup.dismiss();
removePromptView();
@@ -732,7 +734,7 @@
public void setSelection(int position) {
DropDownListView list = mDropDownList;
if (isShowing() && list != null) {
- list.mListSelectionHidden = false;
+ list.setListSelectionHidden(false);
list.setSelection(position);
if (list.getChoiceMode() != ListView.CHOICE_MODE_NONE) {
list.setItemChecked(position, true);
@@ -748,7 +750,7 @@
final DropDownListView list = mDropDownList;
if (list != null) {
// WARNING: Please read the comment where mListSelectionHidden is declared
- list.mListSelectionHidden = true;
+ list.setListSelectionHidden(true);
list.hideSelector();
list.requestLayout();
}
@@ -757,6 +759,7 @@
/**
* @return {@code true} if the popup is currently showing, {@code false} otherwise.
*/
+ @Override
public boolean isShowing() {
return mPopup.isShowing();
}
@@ -842,6 +845,7 @@
* @return The {@link ListView} displayed within the popup window.
* Only valid when {@link #isShowing()} == {@code true}.
*/
+ @Override
public ListView getListView() {
return mDropDownList;
}
@@ -911,7 +915,7 @@
} else {
// WARNING: Please read the comment where mListSelectionHidden
// is declared
- mDropDownList.mListSelectionHidden = false;
+ mDropDownList.setListSelectionHidden(false);
}
consumed = mDropDownList.onKeyDown(keyCode, event);
@@ -1037,7 +1041,7 @@
public OnTouchListener createDragToOpenListener(View src) {
return new ForwardingListener(src) {
@Override
- public ListPopupWindow getPopup() {
+ public ShowableListMenu getPopup() {
return ListPopupWindow.this;
}
};
@@ -1088,7 +1092,7 @@
DropDownListView dropDownList = mDropDownList;
if (dropDownList != null) {
- dropDownList.mListSelectionHidden = false;
+ dropDownList.setListSelectionHidden(false);
}
}
}
@@ -1219,568 +1223,6 @@
return listContent + otherHeights;
}
- /**
- * Abstract class that forwards touch events to a {@link ListPopupWindow}.
- *
- * @hide
- */
- public static abstract class ForwardingListener
- implements View.OnTouchListener, View.OnAttachStateChangeListener {
- /** Scaled touch slop, used for detecting movement outside bounds. */
- private final float mScaledTouchSlop;
-
- /** Timeout before disallowing intercept on the source's parent. */
- private final int mTapTimeout;
-
- /** Timeout before accepting a long-press to start forwarding. */
- private final int mLongPressTimeout;
-
- /** Source view from which events are forwarded. */
- private final View mSrc;
-
- /** Runnable used to prevent conflicts with scrolling parents. */
- private Runnable mDisallowIntercept;
-
- /** Runnable used to trigger forwarding on long-press. */
- private Runnable mTriggerLongPress;
-
- /** Whether this listener is currently forwarding touch events. */
- private boolean mForwarding;
-
- /**
- * Whether forwarding was initiated by a long-press. If so, we won't
- * force the window to dismiss when the touch stream ends.
- */
- private boolean mWasLongPress;
-
- /** The id of the first pointer down in the current event stream. */
- private int mActivePointerId;
-
- public ForwardingListener(View src) {
- mSrc = src;
- mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop();
- mTapTimeout = ViewConfiguration.getTapTimeout();
-
- // Use a medium-press timeout. Halfway between tap and long-press.
- mLongPressTimeout = (mTapTimeout + ViewConfiguration.getLongPressTimeout()) / 2;
-
- src.addOnAttachStateChangeListener(this);
- }
-
- /**
- * Returns the popup to which this listener is forwarding events.
- * <p>
- * Override this to return the correct popup. If the popup is displayed
- * asynchronously, you may also need to override
- * {@link #onForwardingStopped} to prevent premature cancelation of
- * forwarding.
- *
- * @return the popup to which this listener is forwarding events
- */
- public abstract ListPopupWindow getPopup();
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- final boolean wasForwarding = mForwarding;
- final boolean forwarding;
- if (wasForwarding) {
- forwarding = onTouchForwarded(event) || !onForwardingStopped();
- } else {
- forwarding = onTouchObserved(event) && onForwardingStarted();
-
- if (forwarding) {
- // Make sure we cancel any ongoing source event stream.
- final long now = SystemClock.uptimeMillis();
- final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL,
- 0.0f, 0.0f, 0);
- mSrc.onTouchEvent(e);
- e.recycle();
- }
- }
-
- mForwarding = forwarding;
- return forwarding || wasForwarding;
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mForwarding = false;
- mActivePointerId = MotionEvent.INVALID_POINTER_ID;
-
- if (mDisallowIntercept != null) {
- mSrc.removeCallbacks(mDisallowIntercept);
- }
- }
-
- /**
- * Called when forwarding would like to start.
- * <p>
- * By default, this will show the popup returned by {@link #getPopup()}.
- * It may be overridden to perform another action, like clicking the
- * source view or preparing the popup before showing it.
- *
- * @return true to start forwarding, false otherwise
- */
- protected boolean onForwardingStarted() {
- final ListPopupWindow popup = getPopup();
- if (popup != null && !popup.isShowing()) {
- popup.show();
- }
- return true;
- }
-
- /**
- * Called when forwarding would like to stop.
- * <p>
- * By default, this will dismiss the popup returned by
- * {@link #getPopup()}. It may be overridden to perform some other
- * action.
- *
- * @return true to stop forwarding, false otherwise
- */
- protected boolean onForwardingStopped() {
- final ListPopupWindow popup = getPopup();
- if (popup != null && popup.isShowing()) {
- popup.dismiss();
- }
- return true;
- }
-
- /**
- * Observes motion events and determines when to start forwarding.
- *
- * @param srcEvent motion event in source view coordinates
- * @return true to start forwarding motion events, false otherwise
- */
- private boolean onTouchObserved(MotionEvent srcEvent) {
- final View src = mSrc;
- if (!src.isEnabled()) {
- return false;
- }
-
- final int actionMasked = srcEvent.getActionMasked();
- switch (actionMasked) {
- case MotionEvent.ACTION_DOWN:
- mActivePointerId = srcEvent.getPointerId(0);
- mWasLongPress = false;
-
- if (mDisallowIntercept == null) {
- mDisallowIntercept = new DisallowIntercept();
- }
- src.postDelayed(mDisallowIntercept, mTapTimeout);
-
- if (mTriggerLongPress == null) {
- mTriggerLongPress = new TriggerLongPress();
- }
- src.postDelayed(mTriggerLongPress, mLongPressTimeout);
- break;
- case MotionEvent.ACTION_MOVE:
- final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId);
- if (activePointerIndex >= 0) {
- final float x = srcEvent.getX(activePointerIndex);
- final float y = srcEvent.getY(activePointerIndex);
-
- // Has the pointer has moved outside of the view?
- if (!src.pointInView(x, y, mScaledTouchSlop)) {
- clearCallbacks();
-
- // Don't let the parent intercept our events.
- src.getParent().requestDisallowInterceptTouchEvent(true);
- return true;
- }
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- clearCallbacks();
- break;
- }
-
- return false;
- }
-
- private void clearCallbacks() {
- if (mTriggerLongPress != null) {
- mSrc.removeCallbacks(mTriggerLongPress);
- }
-
- if (mDisallowIntercept != null) {
- mSrc.removeCallbacks(mDisallowIntercept);
- }
- }
-
- private void onLongPress() {
- clearCallbacks();
-
- final View src = mSrc;
- if (!src.isEnabled() || src.isLongClickable()) {
- // Ignore long-press if the view is disabled or has its own
- // handler.
- return;
- }
-
- if (!onForwardingStarted()) {
- return;
- }
-
- // Don't let the parent intercept our events.
- src.getParent().requestDisallowInterceptTouchEvent(true);
-
- // Make sure we cancel any ongoing source event stream.
- final long now = SystemClock.uptimeMillis();
- final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
- src.onTouchEvent(e);
- e.recycle();
-
- mForwarding = true;
- mWasLongPress = true;
- }
-
- /**
- * Handled forwarded motion events and determines when to stop
- * forwarding.
- *
- * @param srcEvent motion event in source view coordinates
- * @return true to continue forwarding motion events, false to cancel
- */
- private boolean onTouchForwarded(MotionEvent srcEvent) {
- final View src = mSrc;
- final ListPopupWindow popup = getPopup();
- if (popup == null || !popup.isShowing()) {
- return false;
- }
-
- final DropDownListView dst = popup.mDropDownList;
- if (dst == null || !dst.isShown()) {
- return false;
- }
-
- // Convert event to destination-local coordinates.
- final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent);
- src.toGlobalMotionEvent(dstEvent);
- dst.toLocalMotionEvent(dstEvent);
-
- // Forward converted event to destination view, then recycle it.
- final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId);
- dstEvent.recycle();
-
- // Always cancel forwarding when the touch stream ends.
- final int action = srcEvent.getActionMasked();
- final boolean keepForwarding = action != MotionEvent.ACTION_UP
- && action != MotionEvent.ACTION_CANCEL;
-
- return handled && keepForwarding;
- }
-
- private class DisallowIntercept implements Runnable {
- @Override
- public void run() {
- final ViewParent parent = mSrc.getParent();
- parent.requestDisallowInterceptTouchEvent(true);
- }
- }
-
- private class TriggerLongPress implements Runnable {
- @Override
- public void run() {
- onLongPress();
- }
- }
- }
-
- /**
- * <p>Wrapper class for a ListView. This wrapper can hijack the focus to
- * make sure the list uses the appropriate drawables and states when
- * displayed on screen within a drop down. The focus is never actually
- * passed to the drop down in this mode; the list only looks focused.</p>
- */
- static class DropDownListView extends ListView {
- /** Duration in milliseconds of the drag-to-open click animation. */
- private static final long CLICK_ANIM_DURATION = 150;
-
- /** Target alpha value for drag-to-open click animation. */
- private static final int CLICK_ANIM_ALPHA = 0x80;
-
- /** Wrapper around Drawable's <code>alpha</code> property. */
- private static final IntProperty<Drawable> DRAWABLE_ALPHA =
- new IntProperty<Drawable>("alpha") {
- @Override
- public void setValue(Drawable object, int value) {
- object.setAlpha(value);
- }
-
- @Override
- public Integer get(Drawable object) {
- return object.getAlpha();
- }
- };
-
- /*
- * WARNING: This is a workaround for a touch mode issue.
- *
- * Touch mode is propagated lazily to windows. This causes problems in
- * the following scenario:
- * - Type something in the AutoCompleteTextView and get some results
- * - Move down with the d-pad to select an item in the list
- * - Move up with the d-pad until the selection disappears
- * - Type more text in the AutoCompleteTextView *using the soft keyboard*
- * and get new results; you are now in touch mode
- * - The selection comes back on the first item in the list, even though
- * the list is supposed to be in touch mode
- *
- * Using the soft keyboard triggers the touch mode change but that change
- * is propagated to our window only after the first list layout, therefore
- * after the list attempts to resurrect the selection.
- *
- * The trick to work around this issue is to pretend the list is in touch
- * mode when we know that the selection should not appear, that is when
- * we know the user moved the selection away from the list.
- *
- * This boolean is set to true whenever we explicitly hide the list's
- * selection and reset to false whenever we know the user moved the
- * selection back to the list.
- *
- * When this boolean is true, isInTouchMode() returns true, otherwise it
- * returns super.isInTouchMode().
- */
- private boolean mListSelectionHidden;
-
- /**
- * True if this wrapper should fake focus.
- */
- private boolean mHijackFocus;
-
- /** Whether to force drawing of the pressed state selector. */
- private boolean mDrawsInPressedState;
-
- /** Current drag-to-open click animation, if any. */
- private Animator mClickAnimation;
-
- /** Helper for drag-to-open auto scrolling. */
- private AbsListViewAutoScroller mScrollHelper;
-
- /**
- * <p>Creates a new list view wrapper.</p>
- *
- * @param context this view's context
- */
- public DropDownListView(Context context, boolean hijackFocus) {
- super(context, null, com.android.internal.R.attr.dropDownListViewStyle);
- mHijackFocus = hijackFocus;
- // TODO: Add an API to control this
- setCacheColorHint(0); // Transparent, since the background drawable could be anything.
- }
-
- /**
- * Handles forwarded events.
- *
- * @param activePointerId id of the pointer that activated forwarding
- * @return whether the event was handled
- */
- public boolean onForwardedEvent(MotionEvent event, int activePointerId) {
- boolean handledEvent = true;
- boolean clearPressedItem = false;
-
- final int actionMasked = event.getActionMasked();
- switch (actionMasked) {
- case MotionEvent.ACTION_CANCEL:
- handledEvent = false;
- break;
- case MotionEvent.ACTION_UP:
- handledEvent = false;
- // $FALL-THROUGH$
- case MotionEvent.ACTION_MOVE:
- final int activeIndex = event.findPointerIndex(activePointerId);
- if (activeIndex < 0) {
- handledEvent = false;
- break;
- }
-
- final int x = (int) event.getX(activeIndex);
- final int y = (int) event.getY(activeIndex);
- final int position = pointToPosition(x, y);
- if (position == INVALID_POSITION) {
- clearPressedItem = true;
- break;
- }
-
- final View child = getChildAt(position - getFirstVisiblePosition());
- setPressedItem(child, position, x, y);
- handledEvent = true;
-
- if (actionMasked == MotionEvent.ACTION_UP) {
- clickPressedItem(child, position);
- }
- break;
- }
-
- // Failure to handle the event cancels forwarding.
- if (!handledEvent || clearPressedItem) {
- clearPressedItem();
- }
-
- // Manage automatic scrolling.
- if (handledEvent) {
- if (mScrollHelper == null) {
- mScrollHelper = new AbsListViewAutoScroller(this);
- }
- mScrollHelper.setEnabled(true);
- mScrollHelper.onTouch(this, event);
- } else if (mScrollHelper != null) {
- mScrollHelper.setEnabled(false);
- }
-
- return handledEvent;
- }
-
- /**
- * Starts an alpha animation on the selector. When the animation ends,
- * the list performs a click on the item.
- */
- private void clickPressedItem(final View child, final int position) {
- final long id = getItemIdAtPosition(position);
- final Animator anim = ObjectAnimator.ofInt(
- mSelector, DRAWABLE_ALPHA, 0xFF, CLICK_ANIM_ALPHA, 0xFF);
- anim.setDuration(CLICK_ANIM_DURATION);
- anim.setInterpolator(new AccelerateDecelerateInterpolator());
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- performItemClick(child, position, id);
- }
- });
- anim.start();
-
- if (mClickAnimation != null) {
- mClickAnimation.cancel();
- }
- mClickAnimation = anim;
- }
-
- private void clearPressedItem() {
- mDrawsInPressedState = false;
- setPressed(false);
- updateSelectorState();
-
- final View motionView = getChildAt(mMotionPosition - mFirstPosition);
- if (motionView != null) {
- motionView.setPressed(false);
- }
-
- if (mClickAnimation != null) {
- mClickAnimation.cancel();
- mClickAnimation = null;
- }
- }
-
- private void setPressedItem(View child, int position, float x, float y) {
- mDrawsInPressedState = true;
-
- // Ordering is essential. First, update the container's pressed state.
- drawableHotspotChanged(x, y);
- if (!isPressed()) {
- setPressed(true);
- }
-
- // Next, run layout if we need to stabilize child positions.
- if (mDataChanged) {
- layoutChildren();
- }
-
- // Manage the pressed view based on motion position. This allows us to
- // play nicely with actual touch and scroll events.
- final View motionView = getChildAt(mMotionPosition - mFirstPosition);
- if (motionView != null && motionView != child && motionView.isPressed()) {
- motionView.setPressed(false);
- }
- mMotionPosition = position;
-
- // Offset for child coordinates.
- final float childX = x - child.getLeft();
- final float childY = y - child.getTop();
- child.drawableHotspotChanged(childX, childY);
- if (!child.isPressed()) {
- child.setPressed(true);
- }
-
- // Ensure that keyboard focus starts from the last touched position.
- setSelectedPositionInt(position);
- positionSelectorLikeTouch(position, child, x, y);
-
- // Refresh the drawable state to reflect the new pressed state,
- // which will also update the selector state.
- refreshDrawableState();
-
- if (mClickAnimation != null) {
- mClickAnimation.cancel();
- mClickAnimation = null;
- }
- }
-
- @Override
- boolean touchModeDrawsInPressedState() {
- return mDrawsInPressedState || super.touchModeDrawsInPressedState();
- }
-
- /**
- * <p>Avoids jarring scrolling effect by ensuring that list elements
- * made of a text view fit on a single line.</p>
- *
- * @param position the item index in the list to get a view for
- * @return the view for the specified item
- */
- @Override
- View obtainView(int position, boolean[] isScrap) {
- View view = super.obtainView(position, isScrap);
-
- if (view instanceof TextView) {
- ((TextView) view).setHorizontallyScrolling(true);
- }
-
- return view;
- }
-
- @Override
- public boolean isInTouchMode() {
- // WARNING: Please read the comment where mListSelectionHidden is declared
- return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
- }
-
- /**
- * <p>Returns the focus state in the drop down.</p>
- *
- * @return true always if hijacking focus
- */
- @Override
- public boolean hasWindowFocus() {
- return mHijackFocus || super.hasWindowFocus();
- }
-
- /**
- * <p>Returns the focus state in the drop down.</p>
- *
- * @return true always if hijacking focus
- */
- @Override
- public boolean isFocused() {
- return mHijackFocus || super.isFocused();
- }
-
- /**
- * <p>Returns the focus state in the drop down.</p>
- *
- * @return true always if hijacking focus
- */
- @Override
- public boolean hasFocus() {
- return mHijackFocus || super.hasFocus();
- }
- }
-
private class PopupDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
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 8d42c73..ba77b1b 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,128 @@
*
* @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
- ListPopupWindow.DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
- return new MenuDropDownListView(context, hijackFocus);
+ DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
+ MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus);
+ view.setHoverListener(this);
+ return view;
}
- static class MenuDropDownListView extends ListPopupWindow.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(-1);
+ setNextSelectedPositionInt(-1);
+
+ ((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) {
+ mHoverListener.onItemHovered(
+ ((MenuAdapter) getAdapter()).getAdapterMenu(), position);
+ }
+
+ return superVal;
}
}
-}
+
+
+}
\ No newline at end of file
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b5fae4e..c3ddec7 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1528,10 +1528,10 @@
protected void drawableStateChanged() {
super.drawableStateChanged();
- final int[] state = getDrawableState();
-
- if (mSelectionDivider != null && mSelectionDivider.isStateful()) {
- mSelectionDivider.setState(state);
+ final Drawable selectionDivider = mSelectionDivider;
+ if (selectionDivider != null && selectionDivider.isStateful()
+ && selectionDivider.setState(getDrawableState())) {
+ invalidateDrawable(selectionDivider);
}
}
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index 1507dfb..34a8439 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -20,6 +20,7 @@
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuPresenter;
+import com.android.internal.view.menu.ShowableListMenu;
import com.android.internal.view.menu.SubMenuBuilder;
import android.annotation.MenuRes;
@@ -30,7 +31,6 @@
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnTouchListener;
-import android.widget.ListPopupWindow.ForwardingListener;
/**
* A PopupMenu displays a {@link Menu} in a modal popup window anchored to a {@link View}.
@@ -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;
@@ -170,7 +173,7 @@
}
@Override
- public ListPopupWindow getPopup() {
+ public ShowableListMenu getPopup() {
// This will be null until show() is called.
return mPopup.getPopup();
}
@@ -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/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 4186c40..fce3754 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1746,13 +1746,20 @@
private void updateDrawableState() {
final int[] state = getDrawableState();
+ boolean changed = false;
- if (mProgressDrawable != null && mProgressDrawable.isStateful()) {
- mProgressDrawable.setState(state);
+ final Drawable progressDrawable = mProgressDrawable;
+ if (progressDrawable != null && progressDrawable.isStateful()) {
+ changed |= progressDrawable.setState(state);
}
- if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) {
- mIndeterminateDrawable.setState(state);
+ final Drawable indeterminateDrawable = mIndeterminateDrawable;
+ if (indeterminateDrawable != null && indeterminateDrawable.isStateful()) {
+ changed |= indeterminateDrawable.setState(state);
+ }
+
+ if (changed) {
+ invalidate();
}
}
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index 25b301f..e241d4c 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -105,9 +105,11 @@
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
- if (mOverlay != null && mOverlay.isStateful()) {
- mOverlay.setState(getDrawableState());
- invalidate();
+
+ final Drawable overlay = mOverlay;
+ if (overlay != null && overlay.isStateful()
+ && overlay.setState(getDrawableState())) {
+ invalidateDrawable(overlay);
}
}
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index f3cf61c..c79e184 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -17,6 +17,7 @@
package android.widget;
import com.android.internal.R;
+import com.android.internal.view.menu.ShowableListMenu;
import android.annotation.DrawableRes;
import android.annotation.Nullable;
@@ -44,7 +45,6 @@
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.ListPopupWindow.ForwardingListener;
import android.widget.PopupWindow.OnDismissListener;
/**
@@ -278,7 +278,7 @@
mPopup = popup;
mForwardingListener = new ForwardingListener(this) {
@Override
- public ListPopupWindow getPopup() {
+ public ShowableListMenu getPopup() {
return popup;
}
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 9319af0..6f3a711 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1339,17 +1339,22 @@
protected void drawableStateChanged() {
super.drawableStateChanged();
- final int[] myDrawableState = getDrawableState();
+ final int[] state = getDrawableState();
+ boolean changed = false;
- if (mThumbDrawable != null) {
- mThumbDrawable.setState(myDrawableState);
+ final Drawable thumbDrawable = mThumbDrawable;
+ if (thumbDrawable != null && thumbDrawable.isStateful()) {
+ changed |= thumbDrawable.setState(state);
}
- if (mTrackDrawable != null) {
- mTrackDrawable.setState(myDrawableState);
+ final Drawable trackDrawable = mTrackDrawable;
+ if (trackDrawable != null && trackDrawable.isStateful()) {
+ changed |= trackDrawable.setState(state);
}
- invalidate();
+ if (changed) {
+ invalidate();
+ }
}
@Override
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7a64377..0712052 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -21,6 +21,7 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.Size;
import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.annotation.XmlRes;
@@ -103,6 +104,7 @@
import android.text.style.UpdateAppearance;
import android.text.util.Linkify;
import android.util.AttributeSet;
+import android.util.LocaleList;
import android.util.Log;
import android.util.TypedValue;
import android.view.AccessibilityIterators.TextSegmentIterator;
@@ -553,7 +555,7 @@
private final TextPaint mTextPaint;
private boolean mUserSetTextScaleX;
private Layout mLayout;
- private boolean mLocaleChanged = false;
+ private boolean mLocalesChanged = false;
@ViewDebug.ExportedProperty(category = "text")
private int mGravity = Gravity.TOP | Gravity.START;
@@ -2817,32 +2819,58 @@
}
/**
- * Get the default {@link Locale} of the text in this TextView.
- * @return the default {@link Locale} of the text in this TextView.
+ * Get the default primary {@link Locale} of the text in this TextView. This will always be
+ * the first member of {@link #getTextLocales()}.
+ * @return the default primary {@link Locale} of the text in this TextView.
*/
+ @NonNull
public Locale getTextLocale() {
return mTextPaint.getTextLocale();
}
/**
- * Set the default {@link Locale} of the text in this TextView to the given value. This value
- * is used to choose appropriate typefaces for ambiguous characters. Typically used for CJK
- * locales to disambiguate Hanzi/Kanji/Hanja characters.
+ * Get the default {@link LocaleList} of the text in this TextView.
+ * @return the default {@link LocaleList} of the text in this TextView.
+ */
+ @NonNull @Size(min=1)
+ public LocaleList getTextLocales() {
+ return mTextPaint.getTextLocales();
+ }
+
+ /**
+ * Set the default {@link LocaleList} of the text in this TextView to a one-member list
+ * containing just the given value.
*
* @param locale the {@link Locale} for drawing text, must not be null.
*
- * @see Paint#setTextLocale
+ * @see #setTextLocales
*/
- public void setTextLocale(Locale locale) {
- mLocaleChanged = true;
+ public void setTextLocale(@NonNull Locale locale) {
+ mLocalesChanged = true;
mTextPaint.setTextLocale(locale);
}
+ /**
+ * Set the default {@link LocaleList} of the text in this TextView to the given value.
+ *
+ * This value is used to choose appropriate typefaces for ambiguous characters (typically used
+ * for CJK locales to disambiguate Hanzi/Kanji/Hanja characters). It also affects
+ * other aspects of text display, including line breaking.
+ *
+ * @param locales the {@link LocaleList} for drawing text, must not be null or empty.
+ *
+ * @see Paint#setTextLocales
+ */
+ public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
+ mLocalesChanged = true;
+ mTextPaint.setTextLocales(locales);
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (!mLocaleChanged) {
- mTextPaint.setTextLocale(Locale.getDefault());
+ if (!mLocalesChanged) {
+ mTextPaint.setTextLocales(LocaleList.getDefault());
}
}
@@ -3968,6 +3996,7 @@
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
+
if (mTextColor != null && mTextColor.isStateful()
|| (mHintTextColor != null && mHintTextColor.isStateful())
|| (mLinkTextColor != null && mLinkTextColor.isStateful())) {
@@ -3977,8 +4006,8 @@
if (mDrawables != null) {
final int[] state = getDrawableState();
for (Drawable dr : mDrawables.mShowing) {
- if (dr != null && dr.isStateful()) {
- dr.setState(state);
+ if (dr != null && dr.isStateful() && dr.setState(state)) {
+ invalidateDrawable(dr);
}
}
}
@@ -3989,9 +4018,8 @@
super.drawableHotspotChanged(x, y);
if (mDrawables != null) {
- final int[] state = getDrawableState();
for (Drawable dr : mDrawables.mShowing) {
- if (dr != null && dr.isStateful()) {
+ if (dr != null) {
dr.setHotspot(x, y);
}
}
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..5b19edf 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);
@@ -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..9272193 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,
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 f178c8c..4f4d3e0 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(mPowerProfile);
+ mCpuPowerCalculator = new CpuPowerCalculator();
}
mCpuPowerCalculator.reset();
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index a3ef612..d62f7a6 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -23,54 +23,14 @@
private static final String TAG = "CpuPowerCalculator";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
- private final double[] mPowerCpuNormal;
-
- /**
- * Reusable array for calculations.
- */
- private final long[] mSpeedStepTimes;
-
- public CpuPowerCalculator(PowerProfile profile) {
- final int speedSteps = profile.getNumSpeedSteps();
- mPowerCpuNormal = new double[speedSteps];
- mSpeedStepTimes = new long[speedSteps];
- for (int p = 0; p < speedSteps; p++) {
- mPowerCpuNormal[p] = profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
- }
- }
-
@Override
public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
long rawUptimeUs, int statsType) {
- final int speedSteps = mSpeedStepTimes.length;
-
- long totalTimeAtSpeeds = 0;
- for (int step = 0; step < speedSteps; step++) {
- mSpeedStepTimes[step] = u.getTimeAtCpuSpeed(step, statsType);
- totalTimeAtSpeeds += mSpeedStepTimes[step];
- }
- totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1);
-
app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
- if (DEBUG && app.cpuTimeMs != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": CPU time " + app.cpuTimeMs + " ms");
- }
-
- double cpuPowerMaMs = 0;
- for (int step = 0; step < speedSteps; step++) {
- final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds;
- final double cpuSpeedStepPower = ratio * app.cpuTimeMs * mPowerCpuNormal[step];
- if (DEBUG && ratio != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
- + step + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
- + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
- }
- cpuPowerMaMs += cpuSpeedStepPower;
- }
-
- if (DEBUG && cpuPowerMaMs != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": cpu total power="
- + BatteryStatsHelper.makemAh(cpuPowerMaMs / (60 * 60 * 1000)));
+ app.cpuPowerMah = (double) u.getCpuPowerMaUs(statsType) / (60.0 * 60.0 * 1000.0 * 1000.0);
+ if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
+ + BatteryStatsHelper.makemAh(app.cpuPowerMah));
}
// Keep track of the package with highest drain.
@@ -108,8 +68,5 @@
// Statistics may not have been gathered yet.
app.cpuTimeMs = app.cpuFgTimeMs;
}
-
- // Convert the CPU power to mAh
- app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9ca937c..af73097 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -179,9 +179,15 @@
static void preload() {
Log.d(TAG, "begin preload");
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses");
preloadClasses();
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources");
preloadResources();
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
preloadOpenGL();
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
@@ -265,6 +271,7 @@
continue;
}
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClass " + line);
try {
if (false) {
Log.v(TAG, "Preloading " + line + "...");
@@ -290,6 +297,7 @@
}
throw new RuntimeException(t);
}
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
}
Log.i(TAG, "...preloaded " + count + " classes in "
@@ -302,7 +310,9 @@
runtime.setTargetHeapUtilization(defaultUtilization);
// Fill in dex caches with classes, fields, and methods brought in by preloading.
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadDexCaches");
runtime.preloadDexCaches();
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
// Bring back root. We'll need it later if we're in the zygote.
if (droppedPriviliges) {
@@ -564,6 +574,7 @@
public static void main(String argv[]) {
try {
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
RuntimeInit.enableDdms();
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
@@ -588,17 +599,23 @@
}
registerZygoteSocket(socketName);
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
// Finish profiling the zygote initialization.
SamplingProfilerIntegration.writeZygoteSnapshot();
// Do an initial gc to clean up after startup
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PostZygoteInitGC");
gcAndFinalize();
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
+
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 55e23b1..ec41447 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -170,10 +170,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 +252,7 @@
private Drawable mBackgroundDrawable;
+ private boolean mLoadEleveation = true;
private float mElevation;
/** Whether window content should be clipped to the background outline. */
@@ -322,6 +328,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 +478,12 @@
}
}
+ public void clearContentView() {
+ if (mNonClientDecorView.getChildCount() > 1) {
+ mNonClientDecorView.removeViewAt(1);
+ }
+ }
+
private void transitionTo(Scene scene) {
if (mContentScene == null) {
scene.enter();
@@ -1395,6 +1417,11 @@
}
@Override
+ public float getElevation() {
+ return mElevation;
+ }
+
+ @Override
public final void setClipToOutline(boolean clipToOutline) {
mClipToOutline = clipToOutline;
if (mDecor != null) {
@@ -1991,7 +2018,7 @@
@Override
public final View getDecorView() {
- if (mDecor == null) {
+ if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
@@ -2265,7 +2292,7 @@
}
}
- private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
+ private static final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
/* package */int mDefaultOpacity = PixelFormat.OPAQUE;
@@ -2335,6 +2362,8 @@
private int mRootScrollY = 0;
+ private PhoneWindow mWindow;
+
public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
@@ -2356,7 +2385,7 @@
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
- mBackgroundFallback.draw(mContentRoot, c, mContentParent);
+ mBackgroundFallback.draw(mWindow.mContentRoot, c, mWindow.mContentParent);
}
@Override
@@ -2368,7 +2397,7 @@
if (isDown && (event.getRepeatCount() == 0)) {
// First handle chording of panel key: if a panel key is held
// but not released, try to execute a shortcut in it.
- if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
+ if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
boolean handled = dispatchKeyShortcutEvent(event);
if (handled) {
return true;
@@ -2377,15 +2406,15 @@
// If a panel is open, perform a shortcut on it without the
// chorded panel key
- if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
- if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
+ if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
+ if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
return true;
}
}
}
- if (!isDestroyed()) {
- final Callback cb = getCallback();
+ if (!mWindow.isDestroyed()) {
+ final Callback cb = mWindow.getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
@@ -2393,28 +2422,28 @@
}
}
- return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
- : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
+ return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
+ : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
@Override
public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
// If the panel is already prepared, then perform the shortcut using it.
boolean handled;
- if (mPreparedPanel != null) {
- handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev,
+ if (mWindow.mPreparedPanel != null) {
+ handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
Menu.FLAG_PERFORM_NO_CLOSE);
if (handled) {
- if (mPreparedPanel != null) {
- mPreparedPanel.isHandled = true;
+ if (mWindow.mPreparedPanel != null) {
+ mWindow.mPreparedPanel.isHandled = true;
}
return true;
}
}
// Shortcut not handled by the panel. Dispatch to the view hierarchy.
- final Callback cb = getCallback();
- handled = cb != null && !isDestroyed() && mFeatureId < 0
+ final Callback cb = mWindow.getCallback();
+ handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
if (handled) {
return true;
@@ -2424,10 +2453,10 @@
// combination such as Control+C. Temporarily prepare the panel then mark it
// unprepared again when finished to ensure that the panel will again be prepared
// the next time it is shown for real.
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
- if (st != null && mPreparedPanel == null) {
- preparePanel(st, ev);
- handled = performPanelShortcut(st, ev.getKeyCode(), ev,
+ PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
+ if (st != null && mWindow.mPreparedPanel == null) {
+ mWindow.preparePanel(st, ev);
+ handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
Menu.FLAG_PERFORM_NO_CLOSE);
st.isPrepared = false;
if (handled) {
@@ -2439,23 +2468,23 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- final Callback cb = getCallback();
- return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
- : super.dispatchTouchEvent(ev);
+ final Callback cb = mWindow.getCallback();
+ return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
@Override
public boolean dispatchTrackballEvent(MotionEvent ev) {
- final Callback cb = getCallback();
- return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev)
- : super.dispatchTrackballEvent(ev);
+ final Callback cb = mWindow.getCallback();
+ return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
}
@Override
public boolean dispatchGenericMotionEvent(MotionEvent ev) {
- final Callback cb = getCallback();
- return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev)
- : super.dispatchGenericMotionEvent(ev);
+ final Callback cb = mWindow.getCallback();
+ return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
}
public boolean superDispatchKeyEvent(KeyEvent event) {
@@ -2495,6 +2524,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,12 +2536,30 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
+ if (mHasNonClientDecor && mWindow.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();
int y = (int)event.getY();
if (isOutOfBounds(x, y)) {
- closePanel(mFeatureId);
+ mWindow.closePanel(mFeatureId);
return true;
}
}
@@ -2534,7 +2585,7 @@
if (action == MotionEvent.ACTION_MOVE) {
if (y > (mDownY+30)) {
Log.i(TAG, "Closing!");
- closePanel(mFeatureId);
+ mWindow.closePanel(mFeatureId);
mWatchingForMenu = false;
return true;
}
@@ -2550,7 +2601,7 @@
if (action == MotionEvent.ACTION_DOWN) {
int y = (int)event.getY();
- if (y >= (getHeight()-5) && !hasChildren()) {
+ if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
Log.i(TAG, "Watchiing!");
mWatchingForMenu = true;
}
@@ -2565,7 +2616,7 @@
if (action == MotionEvent.ACTION_MOVE) {
if (y < (getHeight()-30)) {
Log.i(TAG, "Opening!");
- openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
+ mWindow.openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
mWatchingForMenu = false;
return true;
@@ -2599,8 +2650,8 @@
@Override
public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
- final Callback cb = getCallback();
- if (cb != null && !isDestroyed()) {
+ final Callback cb = mWindow.getCallback();
+ if (cb != null && !mWindow.isDestroyed()) {
if (cb.dispatchPopulateAccessibilityEvent(event)) {
return true;
}
@@ -2637,7 +2688,7 @@
if (SWEEP_OPEN_MENU) {
if (mMenuBackground == null && mFeatureId < 0
- && getAttributes().height
+ && mWindow.getAttributes().height
== WindowManager.LayoutParams.MATCH_PARENT) {
mMenuBackground = getContext().getDrawable(
R.drawable.menu_background);
@@ -2662,7 +2713,8 @@
boolean fixedWidth = false;
if (widthMode == AT_MOST) {
- final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
+ final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor
+ : mWindow.mFixedWidthMajor;
if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
final int w;
if (tvw.type == TypedValue.TYPE_DIMENSION) {
@@ -2683,7 +2735,8 @@
}
if (heightMode == AT_MOST) {
- final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
+ final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
+ : mWindow.mFixedHeightMinor;
if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
final int h;
if (tvh.type == TypedValue.TYPE_DIMENSION) {
@@ -2701,21 +2754,21 @@
}
}
- getOutsets(mOutsets);
- if (mOutsets.top > 0 || mOutsets.bottom > 0) {
+ getOutsets(mWindow.mOutsets);
+ if (mWindow.mOutsets.top > 0 || mWindow.mOutsets.bottom > 0) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode != MeasureSpec.UNSPECIFIED) {
int height = MeasureSpec.getSize(heightMeasureSpec);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(
- height + mOutsets.top + mOutsets.bottom, mode);
+ height + mWindow.mOutsets.top + mWindow.mOutsets.bottom, mode);
}
}
- if (mOutsets.left > 0 || mOutsets.right > 0) {
+ if (mWindow.mOutsets.left > 0 || mWindow.mOutsets.right > 0) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
if (mode != MeasureSpec.UNSPECIFIED) {
int width = MeasureSpec.getSize(widthMeasureSpec);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(
- width + mOutsets.left + mOutsets.right, mode);
+ width + mWindow.mOutsets.left + mWindow.mOutsets.right, mode);
}
}
@@ -2727,7 +2780,7 @@
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
if (!fixedWidth && widthMode == AT_MOST) {
- final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
+ final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
if (tv.type != TypedValue.TYPE_NULL) {
final int min;
if (tv.type == TypedValue.TYPE_DIMENSION) {
@@ -2755,12 +2808,12 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- getOutsets(mOutsets);
- if (mOutsets.left > 0) {
- offsetLeftAndRight(-mOutsets.left);
+ getOutsets(mWindow.mOutsets);
+ if (mWindow.mOutsets.left > 0) {
+ offsetLeftAndRight(-mWindow.mOutsets.left);
}
- if (mOutsets.top > 0) {
- offsetTopAndBottom(-mOutsets.top);
+ if (mWindow.mOutsets.top > 0) {
+ offsetTopAndBottom(-mWindow.mOutsets.top);
}
}
@@ -2776,23 +2829,23 @@
@Override
public boolean showContextMenuForChild(View originalView) {
// Reuse the context menu builder
- if (mContextMenu == null) {
- mContextMenu = new ContextMenuBuilder(getContext());
- mContextMenu.setCallback(mContextMenuCallback);
+ if (mWindow.mContextMenu == null) {
+ mWindow.mContextMenu = new ContextMenuBuilder(getContext());
+ mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
} else {
- mContextMenu.clearAll();
+ mWindow.mContextMenu.clearAll();
}
- final MenuDialogHelper helper = mContextMenu.show(originalView,
+ final MenuDialogHelper helper = mWindow.mContextMenu.show(originalView,
originalView.getWindowToken());
if (helper != null) {
- helper.setPresenterCallback(mContextMenuCallback);
- } else if (mContextMenuHelper != null) {
+ helper.setPresenterCallback(mWindow.mContextMenuCallback);
+ } else if (mWindow.mContextMenuHelper != null) {
// No menu to show, but if we have a menu currently showing it just became blank.
// Close it.
- mContextMenuHelper.dismiss();
+ mWindow.mContextMenuHelper.dismiss();
}
- mContextMenuHelper = helper;
+ mWindow.mContextMenuHelper = helper;
return helper != null;
}
@@ -2822,14 +2875,15 @@
View originatingView, ActionMode.Callback callback, int type) {
ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
ActionMode mode = null;
- if (getCallback() != null && !isDestroyed()) {
+ if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
try {
- mode = getCallback().onWindowStartingActionMode(wrappedCallback, type);
+ mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
} catch (AbstractMethodError ame) {
// Older apps might not implement the typed version of this method.
if (type == ActionMode.TYPE_PRIMARY) {
try {
- mode = getCallback().onWindowStartingActionMode(wrappedCallback);
+ mode = mWindow.getCallback().onWindowStartingActionMode(
+ wrappedCallback);
} catch (AbstractMethodError ame2) {
// Older apps might not implement this callback method at all.
}
@@ -2854,9 +2908,9 @@
mode = null;
}
}
- if (mode != null && getCallback() != null && !isDestroyed()) {
+ if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
try {
- getCallback().onActionModeStarted(mode);
+ mWindow.getCallback().onActionModeStarted(mode);
} catch (AbstractMethodError ame) {
// Older apps might not implement this callback method.
}
@@ -2945,10 +2999,10 @@
}
private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
- WindowManager.LayoutParams attrs = getAttributes();
+ WindowManager.LayoutParams attrs = mWindow.getAttributes();
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
- if (!mIsFloating && ActivityManager.isHighEndGfx()) {
+ if (!mWindow.mIsFloating && ActivityManager.isHighEndGfx()) {
boolean disallowAnimate = !isLaidOut();
disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
& FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
@@ -2980,14 +3034,14 @@
boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0;
int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset;
- updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor,
- navBarSize, navBarToRightEdge, 0 /* rightInset */,
- animate && !disallowAnimate);
+ updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
+ mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
+ 0 /* rightInset */, animate && !disallowAnimate);
boolean statusBarNeedsRightInset = navBarToRightEdge
&& mNavigationColorViewState.present;
int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
- updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
+ updateColorViewInt(mStatusColorViewState, sysUiVisibility, mWindow.mStatusBarColor,
mLastTopInset, false /* matchVertical */, statusBarRightInset,
animate && !disallowAnimate);
}
@@ -3004,13 +3058,13 @@
int consumedRight = consumingNavBar ? mLastRightInset : 0;
int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
- if (mContentRoot != null
- && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
- MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
+ if (mWindow.mContentRoot != null
+ && mWindow.mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
+ MarginLayoutParams lp = (MarginLayoutParams) mWindow.mContentRoot.getLayoutParams();
if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) {
lp.rightMargin = consumedRight;
lp.bottomMargin = consumedBottom;
- mContentRoot.setLayoutParams(lp);
+ mWindow.mContentRoot.setLayoutParams(lp);
if (insets == null) {
// The insets have changed, but we're not currently in the process
@@ -3048,11 +3102,11 @@
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
int size, boolean verticalBar, int rightMargin, boolean animate) {
state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
- && (getAttributes().flags & state.hideWindowFlag) == 0
- && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
+ && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
boolean show = state.present
&& (color & Color.BLACK) != 0
- && (getAttributes().flags & state.translucentFlag) == 0;
+ && (mWindow.getAttributes().flags & state.translucentFlag) == 0;
boolean visibilityChanged = false;
View view = state.view;
@@ -3144,14 +3198,14 @@
mPrimaryActionModeView.getLayoutParams();
boolean mlpChanged = false;
if (mPrimaryActionModeView.isShown()) {
- if (mTempRect == null) {
- mTempRect = new Rect();
+ if (mWindow.mTempRect == null) {
+ mWindow.mTempRect = new Rect();
}
- final Rect rect = mTempRect;
+ final Rect rect = mWindow.mTempRect;
// If the parent doesn't consume the insets, manually
// apply the default system window insets.
- mContentParent.computeSystemWindowInsets(insets, rect);
+ mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
if (mlp.topMargin != newMargin) {
mlpChanged = true;
@@ -3182,7 +3236,7 @@
// mode is overlaid on the app content (e.g. it's
// sitting in a FrameLayout, see
// screen_simple_overlay_action_mode.xml).
- final boolean nonOverlay = (getLocalFeatures()
+ final boolean nonOverlay = (mWindow.getLocalFeatures()
& (1 << FEATURE_ACTION_MODE_OVERLAY)) == 0;
insets = insets.consumeSystemWindowInsets(
false, nonOverlay && showStatusGuard /* top */, false, false);
@@ -3206,14 +3260,14 @@
private void updateNavigationGuard(WindowInsets insets) {
// IMEs lay out below the nav bar, but the content view must not (for back compat)
- if (getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
+ if (mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
// prevent the content view from including the nav bar height
- if (mContentParent != null) {
- if (mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
+ if (mWindow.mContentParent != null) {
+ if (mWindow.mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
MarginLayoutParams mlp =
- (MarginLayoutParams) mContentParent.getLayoutParams();
+ (MarginLayoutParams) mWindow.mContentParent.getLayoutParams();
mlp.bottomMargin = insets.getSystemWindowInsetBottom();
- mContentParent.setLayoutParams(mlp);
+ mWindow.mContentParent.setLayoutParams(mlp);
}
}
// position the navigation guard view, creating it if necessary
@@ -3295,7 +3349,7 @@
mDefaultOpacity = opacity;
if (mFeatureId < 0) {
- setDefaultWindowFormat(opacity);
+ mWindow.setDefaultWindowFormat(opacity);
}
}
@@ -3305,12 +3359,13 @@
// If the user is chording a menu shortcut, release the chord since
// this window lost focus
- if (hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus && mPanelChordingKey != 0) {
- closePanel(FEATURE_OPTIONS_PANEL);
+ if (mWindow.hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus
+ && mWindow.mPanelChordingKey != 0) {
+ mWindow.closePanel(FEATURE_OPTIONS_PANEL);
}
- final Callback cb = getCallback();
- if (cb != null && !isDestroyed() && mFeatureId < 0) {
+ final Callback cb = mWindow.getCallback();
+ if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
cb.onWindowFocusChanged(hasWindowFocus);
}
@@ -3326,8 +3381,8 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- final Callback cb = getCallback();
- if (cb != null && !isDestroyed() && mFeatureId < 0) {
+ final Callback cb = mWindow.getCallback();
+ if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
cb.onAttachedToWindow();
}
@@ -3339,7 +3394,7 @@
* menu was open. When the activity is recreated, the menu
* should be shown again.
*/
- openPanelsAfterRestore();
+ mWindow.openPanelsAfterRestore();
}
}
@@ -3347,13 +3402,13 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- final Callback cb = getCallback();
+ final Callback cb = mWindow.getCallback();
if (cb != null && mFeatureId < 0) {
cb.onDetachedFromWindow();
}
- if (mDecorContentParent != null) {
- mDecorContentParent.dismissPopups();
+ if (mWindow.mDecorContentParent != null) {
+ mWindow.mDecorContentParent.dismissPopups();
}
if (mPrimaryActionModePopup != null) {
@@ -3368,7 +3423,7 @@
mFloatingToolbar = null;
}
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+ PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
if (st != null && st.menu != null && mFeatureId < 0) {
st.menu.close();
}
@@ -3377,29 +3432,29 @@
@Override
public void onCloseSystemDialogs(String reason) {
if (mFeatureId >= 0) {
- closeAllPanels();
+ mWindow.closeAllPanels();
}
}
public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
- return mFeatureId < 0 ? mTakeSurfaceCallback : null;
+ return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
}
public InputQueue.Callback willYouTakeTheInputQueue() {
- return mFeatureId < 0 ? mTakeInputQueueCallback : null;
+ return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
}
public void setSurfaceType(int type) {
- PhoneWindow.this.setType(type);
+ mWindow.setType(type);
}
public void setSurfaceFormat(int format) {
- PhoneWindow.this.setFormat(format);
+ mWindow.setFormat(format);
}
public void setSurfaceKeepScreenOn(boolean keepOn) {
- if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
@@ -3431,7 +3486,7 @@
endOnGoingFadeAnimation();
cleanupPrimaryActionMode();
if (mPrimaryActionModeView == null) {
- if (isFloating()) {
+ if (mWindow.isFloating()) {
// Use the action bar theme.
final TypedValue outValue = new TypedValue();
final Theme baseTheme = mContext.getTheme();
@@ -3578,7 +3633,7 @@
private void setHandledFloatingActionMode(ActionMode mode) {
mFloatingActionMode = mode;
- mFloatingToolbar = new FloatingToolbar(mContext, PhoneWindow.this);
+ mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
mFloatingActionModeOriginatingView.getViewTreeObserver()
@@ -3617,6 +3672,10 @@
return windowHasNonClientDecor() && getElevation() > 0;
}
+ void setWindow(PhoneWindow phoneWindow) {
+ mWindow = phoneWindow;
+ }
+
/**
* Clears out internal references when the action mode is destroyed.
*/
@@ -3705,9 +3764,9 @@
cleanupFloatingActionModeViews();
mFloatingActionMode = null;
}
- if (getCallback() != null && !isDestroyed()) {
+ if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
try {
- getCallback().onActionModeFinished(mode);
+ mWindow.getCallback().onActionModeFinished(mode);
} catch (AbstractMethodError ame) {
// Older apps might not implement this callback method.
}
@@ -3727,7 +3786,14 @@
}
protected DecorView generateDecor(int featureId) {
- return new DecorView(getContext(), featureId);
+ // System process doesn't have application context and in that case we need to directly use
+ // the context we have. Otherwise we want the application context, so we don't cling to the
+ // activity.
+ Context context = getContext().getApplicationContext();
+ if (context == null) {
+ context = getContext();
+ }
+ return new DecorView(context, featureId);
}
protected void setFeatureFromAttrs(int featureId, TypedArray attrs,
@@ -3937,7 +4003,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 +4077,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 +4143,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 +4161,28 @@
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);
+ // We can't use the application context inside the general inflater, because some
+ // views might depend on the fact that they get Activity or even specific activity.
+ // We control the NonClientDecor, so we know that application context should be
+ // safe enough.
+ LayoutInflater inflater =
+ mLayoutInflater.cloneInContext(getContext().getApplicationContext());
+ if (Color.luminance(value.data) < 0.5) {
+ nonClientDecorView = (NonClientDecorView) inflater.inflate(
+ R.layout.non_client_decor_dark, null);
+ } else {
+ nonClientDecorView = (NonClientDecorView) inflater.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,14 +4193,17 @@
}
private void installDecor() {
+ mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
+ mDecor.setWindow(this);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
+ mDecor.setWindow(this);
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
@@ -5284,4 +5372,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/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 9761661..44df0ce 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -76,6 +76,15 @@
mMenu = new MenuBuilder(context).setDefaultShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM);
setType(ActionMode.TYPE_FLOATING);
+ mMenu.setCallback(new MenuBuilder.Callback() {
+ @Override
+ public void onMenuModeChange(MenuBuilder menu) {}
+
+ @Override
+ public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return mCallback.onActionItemClicked(FloatingActionMode.this, item);
+ }
+ });
mContentRect = new Rect();
mContentRectOnScreen = new Rect();
mPreviousContentRectOnScreen = new Rect();
@@ -99,7 +108,7 @@
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
- return mCallback.onActionItemClicked(FloatingActionMode.this, item);
+ return mMenu.performItemAction(item, 0);
}
});
mFloatingToolbarVisibilityHelper = new FloatingToolbarVisibilityHelper(mFloatingToolbar);
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index 8db363d..ce5bc90 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -29,10 +29,10 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ActionMenuView;
+import android.widget.ForwardingListener;
import android.widget.ListPopupWindow;
import android.widget.TextView;
import android.widget.Toast;
-import android.widget.ListPopupWindow.ForwardingListener;
/**
* @hide
@@ -320,7 +320,7 @@
}
@Override
- public ListPopupWindow getPopup() {
+ public ShowableListMenu getPopup() {
if (mPopupCallback != null) {
return mPopupCallback.getPopup();
}
@@ -331,7 +331,7 @@
protected boolean onForwardingStarted() {
// Call the invoker, then check if the expected popup is showing.
if (mItemInvoker != null && mItemInvoker.invokeItem(mItemData)) {
- final ListPopupWindow popup = getPopup();
+ final ShowableListMenu popup = getPopup();
return popup != null && popup.isShowing();
}
return false;
@@ -339,7 +339,7 @@
@Override
protected boolean onForwardingStopped() {
- final ListPopupWindow popup = getPopup();
+ final ShowableListMenu popup = getPopup();
if (popup != null) {
popup.dismiss();
return true;
@@ -349,6 +349,6 @@
}
public static abstract class PopupCallback {
- public abstract ListPopupWindow getPopup();
+ public abstract ShowableListMenu getPopup();
}
}
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..415f325
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -0,0 +1,543 @@
+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.AdapterView;
+import android.widget.DropDownListView;
+import android.widget.MenuItemHoverListener;
+import android.widget.ListView;
+import android.widget.MenuPopupWindow;
+import android.widget.MenuPopupWindow.MenuDropDownListView;
+import android.widget.PopupWindow;
+import android.widget.PopupWindow.OnDismissListener;
+
+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 AdapterView.OnItemClickListener,
+ 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 = (MenuAdapter) 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 =
+ (MenuAdapter) 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 = (MenuAdapter) nextView.getAdapter();
+
+ view.clearSelection();
+
+ 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.
+ mPopupWindows.get(nextMenuIndex).setExitTransition(null);
+ 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 List<int[]> mOffsets;
+ private int mPreferredPosition;
+ private boolean mForceShowIcon;
+ 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();
+ mPreferredPosition = mLayoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT :
+ HORIZ_POSITION_RIGHT;
+ 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[]>();
+ 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();
+ mListViews.add((DropDownListView) popupWindow.getListView());
+ }
+
+ 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 void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ MenuAdapter adapter = (MenuAdapter) parent.getAdapter();
+ adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
+ }
+
+ @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 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 (mPreferredPosition == 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;
+ mPreferredPosition = 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();
+ }
+
+ 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();
+ mListViews.add((DropDownListView) popupWindow.getListView());
+ }
+
+ int[] offsets = {x, y};
+ mOffsets.add(offsets);
+ }
+
+ /**
+ * @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);
+ MenuAdapter adapter = (MenuAdapter) view.getAdapter();
+ adapter.mAdapterMenu.close();
+ }
+ }
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ for (ListView view : mListViews) {
+ ((MenuAdapter) 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 (((MenuAdapter) 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 = (MenuAdapter) 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();
+
+ // If there's still a menu open, refocus the new leaf [sub]menu.
+ if (mListViews.size() > 0) {
+ mListViews.get(mListViews.size() - 1).requestFocus();
+ }
+ }
+
+ 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;
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 29ac3f3..2526393 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -43,11 +43,13 @@
private TextView mTitleView;
private CheckBox mCheckBox;
private TextView mShortcutView;
+ private ImageView mSubMenuArrowView;
private Drawable mBackground;
private int mTextAppearance;
private Context mTextAppearanceContext;
private boolean mPreserveIconSpacing;
+ private Drawable mSubMenuArrow;
private int mMenuType;
@@ -68,6 +70,7 @@
mPreserveIconSpacing = a.getBoolean(
com.android.internal.R.styleable.MenuView_preserveIconSpacing, false);
mTextAppearanceContext = context;
+ mSubMenuArrow = a.getDrawable(com.android.internal.R.styleable.MenuView_subMenuArrow);
a.recycle();
}
@@ -77,7 +80,7 @@
}
public ListMenuItemView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
+ this(context, attrs, com.android.internal.R.attr.listMenuViewStyle);
}
@Override
@@ -93,6 +96,10 @@
}
mShortcutView = (TextView) findViewById(com.android.internal.R.id.shortcut);
+ mSubMenuArrowView = (ImageView) findViewById(com.android.internal.R.id.submenuarrow);
+ if (mSubMenuArrowView != null) {
+ mSubMenuArrowView.setImageDrawable(mSubMenuArrow);
+ }
}
public void initialize(MenuItemImpl itemData, int menuType) {
@@ -106,6 +113,7 @@
setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
setIcon(itemData.getIcon());
setEnabled(itemData.isEnabled());
+ setSubMenuArrowVisible(itemData.hasSubMenu());
}
public void setForceShowIcon(boolean forceShow) {
@@ -186,6 +194,12 @@
compoundButton.setChecked(checked);
}
+ private void setSubMenuArrowVisible(boolean hasSubmenu) {
+ if (mSubMenuArrowView != null) {
+ mSubMenuArrowView.setVisibility(hasSubmenu ? View.VISIBLE : View.GONE);
+ }
+ }
+
public void setShortcut(boolean showShortcut, char shortcutKey) {
final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
? VISIBLE : GONE;
diff --git a/core/java/com/android/internal/view/menu/MenuAdapter.java b/core/java/com/android/internal/view/menu/MenuAdapter.java
new file mode 100644
index 0000000..673cfd1
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/MenuAdapter.java
@@ -0,0 +1,113 @@
+/*
+ * 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.internal.view.menu;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import java.util.ArrayList;
+
+public class MenuAdapter extends BaseAdapter {
+ static final int ITEM_LAYOUT = com.android.internal.R.layout.popup_menu_item_layout;
+
+ MenuBuilder mAdapterMenu;
+
+ private int mExpandedIndex = -1;
+
+ private boolean mForceShowIcon;
+ private final boolean mOverflowOnly;
+ private final LayoutInflater mInflater;
+
+ public MenuAdapter(MenuBuilder menu, LayoutInflater inflater, boolean overflowOnly) {
+ mOverflowOnly = overflowOnly;
+ mInflater = inflater;
+ mAdapterMenu = menu;
+ findExpandedIndex();
+ }
+
+ public boolean getForceShowIcon() {
+ return mForceShowIcon;
+ }
+
+ public void setForceShowIcon(boolean forceShow) {
+ mForceShowIcon = forceShow;
+ }
+
+ public int getCount() {
+ ArrayList<MenuItemImpl> items = mOverflowOnly ?
+ mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
+ if (mExpandedIndex < 0) {
+ return items.size();
+ }
+ return items.size() - 1;
+ }
+
+ public MenuBuilder getAdapterMenu() {
+ return mAdapterMenu;
+ }
+
+ public MenuItemImpl getItem(int position) {
+ ArrayList<MenuItemImpl> items = mOverflowOnly ?
+ mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
+ if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
+ position++;
+ }
+ return items.get(position);
+ }
+
+ public long getItemId(int position) {
+ // Since a menu item's ID is optional, we'll use the position as an
+ // ID for the item in the AdapterView
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
+ }
+
+ MenuView.ItemView itemView = (MenuView.ItemView) convertView;
+ if (mForceShowIcon) {
+ ((ListMenuItemView) convertView).setForceShowIcon(true);
+ }
+ itemView.initialize(getItem(position), 0);
+ return convertView;
+ }
+
+ void findExpandedIndex() {
+ final MenuItemImpl expandedItem = mAdapterMenu.getExpandedItem();
+ if (expandedItem != null) {
+ final ArrayList<MenuItemImpl> items = mAdapterMenu.getNonActionItems();
+ final int count = items.size();
+ for (int i = 0; i < count; i++) {
+ final MenuItemImpl item = items.get(i);
+ if (item == expandedItem) {
+ mExpandedIndex = i;
+ return;
+ }
+ }
+ }
+ mExpandedIndex = -1;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ findExpandedIndex();
+ super.notifyDataSetChanged();
+ }
+}
\ No newline at end of file
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/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java
new file mode 100644
index 0000000..b43e8ad
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/MenuPopup.java
@@ -0,0 +1,124 @@
+/*
+ * 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.internal.view.menu;
+
+import android.content.Context;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ListAdapter;
+import android.widget.PopupWindow;
+
+/**
+ * Base class for a menu popup abstraction - i.e., some type of menu, housed in a popup window
+ * environment.
+ *
+ * @hide
+ */
+public abstract class MenuPopup implements ShowableListMenu, MenuPresenter {
+
+ public abstract void setForceShowIcon(boolean forceShow);
+
+ /**
+ * 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
+ */
+ public abstract void addMenu(MenuBuilder menu);
+
+ public abstract void setGravity(int dropDownGravity);
+
+ public abstract void setAnchorView(View anchor);
+
+ /**
+ * Set a listener to receive a callback when the popup is dismissed.
+ *
+ * @param listener Listener that will be notified when the popup is dismissed.
+ */
+ public abstract void setOnDismissListener(PopupWindow.OnDismissListener listener);
+
+ @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("MenuPopups manage their own views");
+ }
+
+ @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;
+ }
+
+ /**
+ * Measures the width of the given menu view.
+ *
+ * @param view The view to measure.
+ * @return The width.
+ */
+ protected static int measureIndividualMenuWidth(ListAdapter adapter, ViewGroup parent,
+ Context context, int maxAllowedWidth) {
+ // Menus don't tend to be long, so this is more sane than it looks.
+ int maxWidth = 0;
+ View itemView = null;
+ int itemType = 0;
+
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ final int positionType = adapter.getItemViewType(i);
+ if (positionType != itemType) {
+ itemType = positionType;
+ itemView = null;
+ }
+
+ if (parent == null) {
+ parent = new FrameLayout(context);
+ }
+
+ itemView = adapter.getView(i, itemView, parent);
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+
+ final int itemWidth = itemView.getMeasuredWidth();
+ if (itemWidth >= maxAllowedWidth) {
+ return maxAllowedWidth;
+ } else if (itemWidth > maxWidth) {
+ maxWidth = itemWidth;
+ }
+ }
+
+ return maxWidth;
+ }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 13654a6..ea79983 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -16,60 +16,30 @@
package com.android.internal.view.menu;
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.Parcelable;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
-import android.widget.ListAdapter;
-import android.widget.MenuPopupWindow;
-import android.widget.PopupWindow;
+import com.android.internal.view.menu.MenuPresenter.Callback;
-import java.util.ArrayList;
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.PopupWindow;
/**
* Presents a menu as a small, simple popup anchored to another view.
+ *
* @hide
*/
-public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
- ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener,
- View.OnAttachStateChangeListener, MenuPresenter {
- static final int ITEM_LAYOUT = com.android.internal.R.layout.popup_menu_item_layout;
-
+public class MenuPopupHelper implements PopupWindow.OnDismissListener {
private final Context mContext;
- private final LayoutInflater mInflater;
private final MenuBuilder mMenu;
- private final MenuAdapter mAdapter;
private final boolean mOverflowOnly;
- private final int mPopupMaxWidth;
private final int mPopupStyleAttr;
private final int mPopupStyleRes;
private View mAnchorView;
- private MenuPopupWindow mPopup;
- private ViewTreeObserver mTreeObserver;
- private Callback mPresenterCallback;
-
- boolean mForceShowIcon;
-
- private ViewGroup mMeasureParent;
-
- /** Whether the cached content width value is valid. */
- private boolean mHasContentWidth;
-
- /** Cached content width from {@link #measureContentWidth}. */
- private int mContentWidth;
+ private MenuPopup mPopup;
private int mDropDownGravity = Gravity.NO_GRAVITY;
+ private Callback mPresenterCallback;
public MenuPopupHelper(Context context, MenuBuilder menu) {
this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0);
@@ -87,33 +57,36 @@
public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView,
boolean overflowOnly, int popupStyleAttr, int popupStyleRes) {
mContext = context;
- mInflater = LayoutInflater.from(context);
mMenu = menu;
- mAdapter = new MenuAdapter(mMenu);
mOverflowOnly = overflowOnly;
mPopupStyleAttr = popupStyleAttr;
mPopupStyleRes = popupStyleRes;
-
- final Resources res = context.getResources();
- mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
- res.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
-
mAnchorView = anchorView;
+ mPopup = createMenuPopup();
+ }
- // Present the menu using our context, not the menu builder's context.
- menu.addMenuPresenter(this, context);
+ private MenuPopup createMenuPopup() {
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableCascadingSubmenus)) {
+ return new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr, mPopupStyleRes,
+ mOverflowOnly);
+ }
+ return new StandardMenuPopup(
+ mContext, mMenu, mAnchorView, mPopupStyleAttr, mPopupStyleRes, mOverflowOnly);
}
public void setAnchorView(View anchor) {
mAnchorView = anchor;
+ mPopup.setAnchorView(anchor);
}
public void setForceShowIcon(boolean forceShow) {
- mForceShowIcon = forceShow;
+ mPopup.setForceShowIcon(forceShow);
}
public void setGravity(int gravity) {
mDropDownGravity = gravity;
+ mPopup.setGravity(gravity);
}
public int getGravity() {
@@ -126,49 +99,38 @@
}
}
- public MenuPopupWindow getPopup() {
+ public ShowableListMenu getPopup() {
return mPopup;
}
/**
- * Attempts to show the popup anchored to the view specified by
- * {@link #setAnchorView(View)}.
+ * Attempts to show the popup anchored to the view specified by {@link #setAnchorView(View)}.
*
- * @return {@code true} if the popup was shown or was already showing prior
- * to calling this method, {@code false} otherwise
+ * @return {@code true} if the popup was shown or was already showing prior to calling this
+ * method, {@code false} otherwise
*/
public boolean tryShow() {
if (isShowing()) {
return true;
}
- mPopup = new MenuPopupWindow(mContext, null, mPopupStyleAttr, mPopupStyleRes);
- mPopup.setOnDismissListener(this);
- mPopup.setOnItemClickListener(this);
- mPopup.setAdapter(mAdapter);
- mPopup.setModal(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.setDropDownGravity(mDropDownGravity);
- } else {
+ if (mAnchorView == null) {
return false;
}
- if (!mHasContentWidth) {
- mContentWidth = measureContentWidth();
- mHasContentWidth = true;
- }
+ mPopup = createMenuPopup();
+ mPopup.setAnchorView(mAnchorView);
+ mPopup.setGravity(mDropDownGravity);
+ mPopup.setCallback(mPresenterCallback);
- mPopup.setContentWidth(mContentWidth);
- mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ // 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
+ // its own handling.
+ mPopup.setOnDismissListener(this);
+
+ mPopup.addMenu(mMenu);
mPopup.show();
- mPopup.getListView().setOnKeyListener(this);
return true;
}
@@ -181,250 +143,15 @@
@Override
public void onDismiss() {
mPopup = null;
- mMenu.close();
- 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 onItemClick(AdapterView<?> parent, View view, int position, long id) {
- MenuAdapter adapter = mAdapter;
- adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
- }
- @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;
- }
-
- private int measureContentWidth() {
- // Menus don't tend to be long, so this is more sane than it looks.
- int maxWidth = 0;
- View itemView = null;
- int itemType = 0;
-
- final ListAdapter adapter = mAdapter;
- final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- final int count = adapter.getCount();
- for (int i = 0; i < count; i++) {
- final int positionType = adapter.getItemViewType(i);
- if (positionType != itemType) {
- itemType = positionType;
- itemView = null;
- }
-
- if (mMeasureParent == null) {
- mMeasureParent = new FrameLayout(mContext);
- }
-
- itemView = adapter.getView(i, itemView, mMeasureParent);
- itemView.measure(widthMeasureSpec, heightMeasureSpec);
-
- final int itemWidth = itemView.getMeasuredWidth();
- if (itemWidth >= mPopupMaxWidth) {
- return mPopupMaxWidth;
- } else if (itemWidth > maxWidth) {
- maxWidth = itemWidth;
- }
- }
-
- return maxWidth;
- }
-
- @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) {
- mHasContentWidth = false;
-
- if (mAdapter != null) {
- mAdapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void setCallback(Callback cb) {
+ public void setCallback(MenuPresenter.Callback cb) {
mPresenterCallback = cb;
- }
-
- @Override
- public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
- if (subMenu.hasVisibleItems()) {
- MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView);
- 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);
-
- if (subPopup.tryShow()) {
- if (mPresenterCallback != null) {
- mPresenterCallback.onOpenSubMenu(subMenu);
- }
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- // Only care about the (sub)menu we're presenting.
- if (menu != mMenu) return;
-
- dismiss();
- if (mPresenterCallback != null) {
- mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
- }
- }
-
- @Override
- public boolean flagActionItems() {
- return false;
- }
-
- public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
- return false;
- }
-
- 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) {
- }
-
- private class MenuAdapter extends BaseAdapter {
- private MenuBuilder mAdapterMenu;
- private int mExpandedIndex = -1;
-
- public MenuAdapter(MenuBuilder menu) {
- mAdapterMenu = menu;
- findExpandedIndex();
- }
-
- public int getCount() {
- ArrayList<MenuItemImpl> items = mOverflowOnly ?
- mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
- if (mExpandedIndex < 0) {
- return items.size();
- }
- return items.size() - 1;
- }
-
- public MenuItemImpl getItem(int position) {
- ArrayList<MenuItemImpl> items = mOverflowOnly ?
- mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
- if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
- position++;
- }
- return items.get(position);
- }
-
- public long getItemId(int position) {
- // Since a menu item's ID is optional, we'll use the position as an
- // ID for the item in the AdapterView
- return position;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
- }
-
- MenuView.ItemView itemView = (MenuView.ItemView) convertView;
- if (mForceShowIcon) {
- ((ListMenuItemView) convertView).setForceShowIcon(true);
- }
- itemView.initialize(getItem(position), 0);
- return convertView;
- }
-
- void findExpandedIndex() {
- final MenuItemImpl expandedItem = mMenu.getExpandedItem();
- if (expandedItem != null) {
- final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
- final int count = items.size();
- for (int i = 0; i < count; i++) {
- final MenuItemImpl item = items.get(i);
- if (item == expandedItem) {
- mExpandedIndex = i;
- return;
- }
- }
- }
- mExpandedIndex = -1;
- }
-
- @Override
- public void notifyDataSetChanged() {
- findExpandedIndex();
- super.notifyDataSetChanged();
- }
+ mPopup.setCallback(cb);
}
}
diff --git a/core/java/com/android/internal/view/menu/ShowableListMenu.java b/core/java/com/android/internal/view/menu/ShowableListMenu.java
new file mode 100644
index 0000000..ca158fd
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/ShowableListMenu.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.internal.view.menu;
+
+import android.widget.ListView;
+
+/**
+ * A list menu which can be shown and hidden and which is internally represented by a ListView.
+ */
+public interface ShowableListMenu {
+ public void show();
+
+ public void dismiss();
+
+ public boolean isShowing();
+
+ /**
+ * @return The internal ListView for the visible menu.
+ */
+ public ListView getListView();
+}
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
new file mode 100644
index 0000000..8877f3d
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -0,0 +1,291 @@
+package com.android.internal.view.menu;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Parcelable;
+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.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.ViewTreeObserver;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.MenuPopupWindow;
+import android.widget.PopupWindow;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.PopupWindow.OnDismissListener;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A standard menu popup in which when a submenu is opened, it replaces its parent menu in the
+ * viewport.
+ */
+final class StandardMenuPopup extends MenuPopup implements OnDismissListener, OnItemClickListener,
+ MenuPresenter, OnKeyListener {
+
+ private final Context mContext;
+ private final LayoutInflater mInflater;
+ private final MenuBuilder mMenu;
+ private final MenuAdapter mAdapter;
+ private final boolean mOverflowOnly;
+ 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 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;
+
+ /** Cached content width. */
+ private int mContentWidth;
+
+ private int mDropDownGravity = Gravity.NO_GRAVITY;
+
+ public StandardMenuPopup(Context context, MenuBuilder menu, View anchorView, int popupStyleAttr,
+ int popupStyleRes, boolean overflowOnly) {
+ mContext = Preconditions.checkNotNull(context);
+ mInflater = LayoutInflater.from(context);
+ mMenu = menu;
+ mOverflowOnly = overflowOnly;
+ mAdapter = new MenuAdapter(menu, mInflater, mOverflowOnly);
+ mPopupStyleAttr = popupStyleAttr;
+ mPopupStyleRes = popupStyleRes;
+
+ final Resources res = context.getResources();
+ mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
+ res.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
+
+ mAnchorView = anchorView;
+
+ mPopup = new MenuPopupWindow(mContext, null, mPopupStyleAttr, mPopupStyleRes);
+
+ // Present the menu using our context, not the menu builder's context.
+ menu.addMenuPresenter(this, context);
+ }
+
+ @Override
+ public void setForceShowIcon(boolean forceShow) {
+ mAdapter.setForceShowIcon(forceShow);
+ }
+
+ @Override
+ public void setGravity(int gravity) {
+ mDropDownGravity = gravity;
+ }
+
+ private boolean tryShow() {
+ if (isShowing()) {
+ 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 = 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(
+ mAdapter, mMeasureParent, mContext, mPopupMaxWidth);
+ mHasContentWidth = true;
+ }
+
+ mPopup.setContentWidth(mContentWidth);
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ mPopup.show();
+ mPopup.getListView().setOnKeyListener(this);
+ return true;
+ }
+
+ @Override
+ public void show() {
+ if (!tryShow()) {
+ throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
+ }
+ }
+
+ @Override
+ public void dismiss() {
+ if (isShowing()) {
+ mPopup.dismiss();
+ }
+ }
+
+ @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 !mWasDismissed && mPopup.isShowing();
+ }
+
+ @Override
+ public void onDismiss() {
+ mWasDismissed = true;
+ mMenu.close();
+
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = mShownAnchorView.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ mTreeObserver = null;
+ }
+ mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
+ mOnDismissListener.onDismiss();
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ mHasContentWidth = false;
+
+ if (mAdapter != null) {
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void setCallback(Callback cb) {
+ mPresenterCallback = cb;
+ }
+
+ @Override
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (subMenu.hasVisibleItems()) {
+ MenuPopupHelper subPopup = new MenuPopupHelper(
+ mContext, subMenu, mShownAnchorView, mOverflowOnly, mPopupStyleAttr,
+ mPopupStyleRes);
+ subPopup.setCallback(mPresenterCallback);
+ subPopup.setForceShowIcon(mAdapter.getForceShowIcon());
+
+ if (subPopup.tryShow()) {
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onOpenSubMenu(subMenu);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ // Only care about the (sub)menu we're presenting.
+ if (menu != mMenu) return;
+
+ dismiss();
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ @Override
+ public boolean flagActionItems() {
+ return false;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
+
+ @Override
+ public void setAnchorView(View anchor) {
+ mAnchorView = anchor;
+ }
+
+ @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;
+ }
+
+ @Override
+ public void setOnDismissListener(OnDismissListener listener) {
+ mOnDismissListener = listener;
+ }
+
+ @Override
+ public ListView getListView() {
+ return mPopup.getListView();
+ }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java
index 35eeca7..582c4f1 100644
--- a/core/java/com/android/internal/widget/AbsActionBarView.java
+++ b/core/java/com/android/internal/widget/AbsActionBarView.java
@@ -19,6 +19,7 @@
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
+import android.view.MotionEvent;
import android.widget.ActionMenuPresenter;
import android.widget.ActionMenuView;
@@ -53,6 +54,9 @@
protected Animator mVisibilityAnim;
+ private boolean mEatingTouch;
+ private boolean mEatingHover;
+
public AbsActionBarView(Context context) {
this(context, null);
}
@@ -97,6 +101,57 @@
}
}
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // ActionBarViews always eat touch events, but should still respect the touch event dispatch
+ // contract. If the normal View implementation doesn't want the events, we'll just silently
+ // eat the rest of the gesture without reporting the events to the default implementation
+ // since that's what it expects.
+
+ final int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mEatingTouch = false;
+ }
+
+ if (!mEatingTouch) {
+ final boolean handled = super.onTouchEvent(ev);
+ if (action == MotionEvent.ACTION_DOWN && !handled) {
+ mEatingTouch = true;
+ }
+ }
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ mEatingTouch = false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent ev) {
+ // Same deal as onTouchEvent() above. Eat all hover events, but still
+ // respect the touch event dispatch contract.
+
+ final int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_HOVER_ENTER) {
+ mEatingHover = false;
+ }
+
+ if (!mEatingHover) {
+ final boolean handled = super.onHoverEvent(ev);
+ if (action == MotionEvent.ACTION_HOVER_ENTER && !handled) {
+ mEatingHover = true;
+ }
+ }
+
+ if (action == MotionEvent.ACTION_HOVER_EXIT
+ || action == MotionEvent.ACTION_CANCEL) {
+ mEatingHover = false;
+ }
+
+ return true;
+ }
+
/**
* Sets whether the bar should be split right now, no questions asked.
* @param split true if the bar should split
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index 1961b4b..398bbe7 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -155,14 +155,27 @@
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
- if (mBackground != null && mBackground.isStateful()) {
- mBackground.setState(getDrawableState());
+
+ final int[] state = getDrawableState();
+ boolean changed = false;
+
+ final Drawable background = mBackground;
+ if (background != null && background.isStateful()) {
+ changed |= background.setState(state);
}
- if (mStackedBackground != null && mStackedBackground.isStateful()) {
- mStackedBackground.setState(getDrawableState());
+
+ final Drawable stackedBackground = mStackedBackground;
+ if (stackedBackground != null && stackedBackground.isStateful()) {
+ changed |= stackedBackground.setState(state);
}
- if (mSplitBackground != null && mSplitBackground.isStateful()) {
- mSplitBackground.setState(getDrawableState());
+
+ final Drawable splitBackground = mSplitBackground;
+ if (splitBackground != null && splitBackground.isStateful()) {
+ changed |= splitBackground.setState(state);
+ }
+
+ if (changed) {
+ invalidate();
}
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 073a2ad..f7e9add 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -232,7 +232,7 @@
public void reportFailedPasswordAttempt(int userId) {
getDevicePolicyManager().reportFailedPasswordAttempt(userId);
getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
- requireCredentialEntry(userId);
+ requireStrongAuth(StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL, userId);
}
public void reportSuccessfulPasswordAttempt(int userId) {
@@ -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) {
+ // 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;
}
@@ -1290,11 +1300,18 @@
*/
public static final int STRONG_AUTH_REQUIRED_AFTER_LOCKOUT = 0x8;
+ /**
+ * Some authentication is required because the user has entered a wrong credential.
+ */
+ public static final int SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL = 0x10;
+
public static final int DEFAULT = STRONG_AUTH_REQUIRED_AFTER_BOOT;
- private static final int ALLOWING_FINGERPRINT = SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
- final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
+ 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();
private final H mHandler;
public StrongAuthTracker() {
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/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index a2c4f6a..6217d6e 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -753,9 +753,10 @@
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
- final Drawable d = mMarginDrawable;
- if (d != null && d.isStateful()) {
- d.setState(getDrawableState());
+ final Drawable marginDrawable = mMarginDrawable;
+ if (marginDrawable != null && marginDrawable.isStateful()
+ && marginDrawable.setState(getDrawableState())) {
+ invalidateDrawable(marginDrawable);
}
}
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/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f7a2f9f..e4bc800 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_DALVIK
#define LOG_TAG "AndroidRuntime"
//#define LOG_NDEBUG 0
@@ -23,6 +24,7 @@
#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <utils/misc.h>
+#include <utils/Trace.h>
#include <binder/Parcel.h>
#include <utils/threads.h>
#include <cutils/properties.h>
@@ -1437,6 +1439,7 @@
*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
+ ATRACE_NAME("RegisterAndroidNatives");
/*
* This hook causes all future threads created in this process to be
* attached to the JavaVM. (This needs to go away in favor of JNI
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index b9e48a0..80de526 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) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 9aa544f..7ca0654 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -510,7 +510,7 @@
}
static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
- jstring path)
+ jstring path, jboolean appAsLib)
{
ScopedUtfChars path8(env, path);
if (path8.c_str() == NULL) {
@@ -523,7 +523,7 @@
}
int32_t cookie;
- bool res = am->addAssetPath(String8(path8.c_str()), &cookie);
+ bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib);
return (res) ? static_cast<jint>(cookie) : 0;
}
@@ -2138,7 +2138,7 @@
(void*) android_content_AssetManager_getAssetLength },
{ "getAssetRemainingLength", "(J)J",
(void*) android_content_AssetManager_getAssetRemainingLength },
- { "addAssetPathNative", "(Ljava/lang/String;)I",
+ { "addAssetPathNative", "(Ljava/lang/String;Z)I",
(void*) android_content_AssetManager_addAssetPath },
{ "addOverlayPathNative", "(Ljava/lang/String;)I",
(void*) android_content_AssetManager_addOverlayPath },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 54be410..d1acb59 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -57,6 +57,7 @@
jfieldID secure;
jfieldID appVsyncOffsetNanos;
jfieldID presentationDeadlineNanos;
+ jfieldID colorTransform;
} gPhysicalDisplayInfoClassInfo;
static struct {
@@ -394,6 +395,8 @@
info.appVsyncOffset);
env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos,
info.presentationDeadline);
+ env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.colorTransform,
+ info.colorTransform);
env->SetObjectArrayElement(configArray, static_cast<jsize>(c), infoObj);
env->DeleteLocalRef(infoObj);
}
@@ -656,6 +659,8 @@
clazz, "appVsyncOffsetNanos", "J");
gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos = GetFieldIDOrDie(env,
clazz, "presentationDeadlineNanos", "J");
+ gPhysicalDisplayInfoClassInfo.colorTransform = GetFieldIDOrDie(env, clazz,
+ "colorTransform", "I");
jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 414cbb4..aef70be 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -83,6 +83,14 @@
pid_t pid;
int status;
+ // It's necessary to save and restore the errno during this function.
+ // Since errno is stored per thread, changing it here modifies the errno
+ // on the thread on which this signal handler executes. If a signal occurs
+ // between a call and an errno check, it's possible to get the errno set
+ // here.
+ // See b/23572286 for extra information.
+ int saved_errno = errno;
+
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
// Log process-death status that we care about. In general it is
// not safe to call LOG(...) from a signal handler because of
@@ -118,6 +126,8 @@
if (pid < 0 && errno != ECHILD) {
ALOGW("Zygote SIGCHLD error in waitpid: %s", strerror(errno));
}
+
+ errno = saved_errno;
}
// Configures the SIGCHLD handler for the zygote process. This is configured
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b0621e9..921385d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1071,6 +1071,11 @@
<permission android:name="android.permission.CONNECTIVITY_INTERNAL"
android:protectionLevel="signature|privileged" />
+ <!-- Allows a system application to access hardware packet offload capabilities.
+ @hide -->
+ <permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi
@hide -->
<permission android:name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"
@@ -2069,6 +2074,12 @@
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
android:protectionLevel="signature" />
+ <!-- Allows an application to monitor changes in tablet mode.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.TABLET_MODE_LISTENER"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to request installing packages. Apps
targeting APIs greater than 22 must hold this permission in
order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}.
diff --git a/core/res/res/anim/ic_checkbox_to_checked_box_inner_merged_animation.xml b/core/res/res/anim/btn_checkbox_to_checked_box_inner_merged_animation.xml
similarity index 92%
rename from core/res/res/anim/ic_checkbox_to_checked_box_inner_merged_animation.xml
rename to core/res/res/anim/btn_checkbox_to_checked_box_inner_merged_animation.xml
index e522453..11d018b 100644
--- a/core/res/res/anim/ic_checkbox_to_checked_box_inner_merged_animation.xml
+++ b/core/res/res/anim/btn_checkbox_to_checked_box_inner_merged_animation.xml
@@ -21,7 +21,7 @@
android:valueFrom="M -7.0,-7.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
android:valueTo="M 0.0,-0.05 l 0.0,0.0 c 0.02761423749,0.0 0.05,0.02238576251 0.05,0.05 l 0.0,0.0 c 0.0,0.02761423749 -0.02238576251,0.05 -0.05,0.05 l 0.0,0.0 c -0.02761423749,0.0 -0.05,-0.02238576251 -0.05,-0.05 l 0.0,0.0 c 0.0,-0.02761423749 0.02238576251,-0.05 0.05,-0.05 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
android:valueType="pathType"
- android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_1" />
<set android:ordering="sequentially">
<objectAnimator
android:duration="166"
@@ -34,6 +34,6 @@
android:propertyName="fillAlpha"
android:valueFrom="1.0"
android:valueTo="0.0"
- android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_0" />
+ android:interpolator="@interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_0" />
</set>
</set>
diff --git a/core/res/res/anim/ic_checkbox_to_checked_box_outer_merged_animation.xml b/core/res/res/anim/btn_checkbox_to_checked_box_outer_merged_animation.xml
similarity index 95%
rename from core/res/res/anim/ic_checkbox_to_checked_box_outer_merged_animation.xml
rename to core/res/res/anim/btn_checkbox_to_checked_box_outer_merged_animation.xml
index 628e967..9e90ba0 100644
--- a/core/res/res/anim/ic_checkbox_to_checked_box_outer_merged_animation.xml
+++ b/core/res/res/anim/btn_checkbox_to_checked_box_outer_merged_animation.xml
@@ -29,7 +29,7 @@
android:valueFrom="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -1.4234161377,-1.40159606934 -1.4234161377,-1.40159606934 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 0.00932312011719,-0.0124053955078 0.00932312011719,-0.0124053955078 c 0.0,0.0 0.0234069824219,-0.0235137939453 0.0234069824219,-0.0235137939453 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -1.4375,1.43751525879 -1.4375,1.43751525879 Z"
android:valueTo="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -5.0,-5.00001525879 -5.0,-5.00001525879 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 3.58590698242,3.58601379395 3.58590698242,3.58601379395 c 0.0,0.0 7.58590698242,-7.58601379395 7.58590698242,-7.58601379395 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -9.0,9.00001525879 -9.0,9.00001525879 Z"
android:valueType="pathType"
- android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_1" />
</set>
<set android:ordering="sequentially">
<objectAnimator
@@ -43,6 +43,6 @@
android:propertyName="fillAlpha"
android:valueFrom="0.0"
android:valueTo="1.0"
- android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_0" />
+ android:interpolator="@interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_0" />
</set>
</set>
diff --git a/core/res/res/anim/ic_checkbox_to_checked_icon_null_animation.xml b/core/res/res/anim/btn_checkbox_to_checked_icon_null_animation.xml
similarity index 78%
rename from core/res/res/anim/ic_checkbox_to_checked_icon_null_animation.xml
rename to core/res/res/anim/btn_checkbox_to_checked_icon_null_animation.xml
index 6fa3fd5..cf2b832 100644
--- a/core/res/res/anim/ic_checkbox_to_checked_icon_null_animation.xml
+++ b/core/res/res/anim/btn_checkbox_to_checked_icon_null_animation.xml
@@ -21,13 +21,13 @@
android:propertyName="scaleX"
android:valueFrom="0.2"
android:valueTo="0.18"
- android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_1" />
<objectAnimator
android:duration="300"
android:propertyName="scaleX"
android:valueFrom="0.18"
android:valueTo="0.2"
- android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_1" />
</set>
<set android:ordering="sequentially">
<objectAnimator
@@ -35,12 +35,12 @@
android:propertyName="scaleY"
android:valueFrom="0.2"
android:valueTo="0.18"
- android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_1" />
<objectAnimator
android:duration="300"
android:propertyName="scaleY"
android:valueFrom="0.18"
android:valueTo="0.2"
- android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_1" />
</set>
</set>
diff --git a/core/res/res/anim/ic_checkbox_to_unchecked_box_inner_merged_animation.xml b/core/res/res/anim/btn_checkbox_to_unchecked_box_inner_merged_animation.xml
similarity index 95%
rename from core/res/res/anim/ic_checkbox_to_unchecked_box_inner_merged_animation.xml
rename to core/res/res/anim/btn_checkbox_to_unchecked_box_inner_merged_animation.xml
index d35b426..97cc268 100644
--- a/core/res/res/anim/ic_checkbox_to_unchecked_box_inner_merged_animation.xml
+++ b/core/res/res/anim/btn_checkbox_to_unchecked_box_inner_merged_animation.xml
@@ -29,7 +29,7 @@
android:valueFrom="M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
android:valueTo="M -7.0,-7.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
android:valueType="pathType"
- android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_checked_mtrl_animation_interpolator_1" />
</set>
<set android:ordering="sequentially">
<objectAnimator
@@ -43,6 +43,6 @@
android:propertyName="fillAlpha"
android:valueFrom="0.0"
android:valueTo="1.0"
- android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_0" />
+ android:interpolator="@interpolator/btn_checkbox_checked_mtrl_animation_interpolator_0" />
</set>
</set>
diff --git a/core/res/res/anim/ic_checkbox_to_unchecked_check_path_merged_animation.xml b/core/res/res/anim/btn_checkbox_to_unchecked_check_path_merged_animation.xml
similarity index 93%
rename from core/res/res/anim/ic_checkbox_to_unchecked_check_path_merged_animation.xml
rename to core/res/res/anim/btn_checkbox_to_unchecked_check_path_merged_animation.xml
index a5d814e..92ab963 100644
--- a/core/res/res/anim/ic_checkbox_to_unchecked_check_path_merged_animation.xml
+++ b/core/res/res/anim/btn_checkbox_to_unchecked_check_path_merged_animation.xml
@@ -21,7 +21,7 @@
android:valueFrom="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -5.0,-5.00001525879 -5.0,-5.00001525879 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 3.58590698242,3.58601379395 3.58590698242,3.58601379395 c 0.0,0.0 7.58590698242,-7.58601379395 7.58590698242,-7.58601379395 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -9.0,9.00001525879 -9.0,9.00001525879 Z"
android:valueTo="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M 0.0,1.42500305176 c 0.0,0.0 -1.4234161377,-1.40159606934 -1.4234161377,-1.40159606934 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 0.00932312011719,-0.0124053955078 0.00932312011719,-0.0124053955078 c 0.0,0.0 0.0234069824219,-0.0235137939453 0.0234069824219,-0.0235137939453 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -1.4375,1.43751525879 -1.4375,1.43751525879 Z"
android:valueType="pathType"
- android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_checked_mtrl_animation_interpolator_1" />
<set android:ordering="sequentially" >
<objectAnimator
android:duration="133"
@@ -34,6 +34,6 @@
android:propertyName="fillAlpha"
android:valueFrom="1.0"
android:valueTo="0.0"
- android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_0" />
+ android:interpolator="@interpolator/btn_checkbox_checked_mtrl_animation_interpolator_0" />
</set>
</set>
diff --git a/core/res/res/anim/ic_checkbox_to_unchecked_icon_null_animation.xml b/core/res/res/anim/btn_checkbox_to_unchecked_icon_null_animation.xml
similarity index 79%
rename from core/res/res/anim/ic_checkbox_to_unchecked_icon_null_animation.xml
rename to core/res/res/anim/btn_checkbox_to_unchecked_icon_null_animation.xml
index 0f07b0e..a4c17d4 100644
--- a/core/res/res/anim/ic_checkbox_to_unchecked_icon_null_animation.xml
+++ b/core/res/res/anim/btn_checkbox_to_unchecked_icon_null_animation.xml
@@ -21,13 +21,13 @@
android:propertyName="scaleX"
android:valueFrom="0.2"
android:valueTo="0.18"
- android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_checked_mtrl_animation_interpolator_1" />
<objectAnimator
android:duration="333"
android:propertyName="scaleX"
android:valueFrom="0.18"
android:valueTo="0.2"
- android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_checked_mtrl_animation_interpolator_1" />
</set>
<set android:ordering="sequentially">
<objectAnimator
@@ -35,12 +35,12 @@
android:propertyName="scaleY"
android:valueFrom="0.2"
android:valueTo="0.18"
- android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_checked_mtrl_animation_interpolator_1" />
<objectAnimator
android:duration="333"
android:propertyName="scaleY"
android:valueFrom="0.18"
android:valueTo="0.2"
- android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+ android:interpolator="@interpolator/btn_checkbox_checked_mtrl_animation_interpolator_1" />
</set>
</set>
diff --git a/core/res/res/anim/btn_radio_to_off_mtrl_dot_group_animation.xml b/core/res/res/anim/btn_radio_to_off_mtrl_dot_group_animation.xml
new file mode 100644
index 0000000..1c6efcf
--- /dev/null
+++ b/core/res/res/anim/btn_radio_to_off_mtrl_dot_group_animation.xml
@@ -0,0 +1,67 @@
+<?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.
+-->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="scaleX"
+ android:valueFrom="1.0"
+ android:valueTo="1.4"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleX"
+ android:valueFrom="1.4"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="scaleX"
+ android:valueFrom="0.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="scaleY"
+ android:valueFrom="1.0"
+ android:valueTo="1.4"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleY"
+ android:valueFrom="1.4"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="scaleY"
+ android:valueFrom="0.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/core/res/res/anim/btn_radio_to_off_mtrl_ring_outer_animation.xml b/core/res/res/anim/btn_radio_to_off_mtrl_ring_outer_animation.xml
new file mode 100644
index 0000000..2b5d9d3
--- /dev/null
+++ b/core/res/res/anim/btn_radio_to_off_mtrl_ring_outer_animation.xml
@@ -0,0 +1,67 @@
+<?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.
+-->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="scaleX"
+ android:valueFrom="1.0"
+ android:valueTo="0.9"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleX"
+ android:valueFrom="0.9"
+ android:valueTo="0.5"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/btn_radio_to_off_mtrl_animation_interpolator_0" />
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="scaleX"
+ android:valueFrom="0.5"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/btn_radio_to_off_mtrl_animation_interpolator_0" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="scaleY"
+ android:valueFrom="1.0"
+ android:valueTo="0.9"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleY"
+ android:valueFrom="0.9"
+ android:valueTo="0.5"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/btn_radio_to_off_mtrl_animation_interpolator_0" />
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="scaleY"
+ android:valueFrom="0.5"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/btn_radio_to_off_mtrl_animation_interpolator_0" />
+ </set>
+</set>
diff --git a/core/res/res/anim/btn_radio_to_off_mtrl_ring_outer_path_animation.xml b/core/res/res/anim/btn_radio_to_off_mtrl_ring_outer_path_animation.xml
new file mode 100644
index 0000000..09be268
--- /dev/null
+++ b/core/res/res/anim/btn_radio_to_off_mtrl_ring_outer_path_animation.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="strokeWidth"
+ android:valueFrom="2.0"
+ android:valueTo="2.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="strokeWidth"
+ android:valueFrom="2.0"
+ android:valueTo="18.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="strokeWidth"
+ android:valueFrom="18.0"
+ android:valueTo="2.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/core/res/res/anim/btn_radio_to_on_mtrl_dot_group_animation.xml b/core/res/res/anim/btn_radio_to_on_mtrl_dot_group_animation.xml
new file mode 100644
index 0000000..ad3a967
--- /dev/null
+++ b/core/res/res/anim/btn_radio_to_on_mtrl_dot_group_animation.xml
@@ -0,0 +1,67 @@
+<?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.
+-->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="166"
+ android:propertyName="scaleX"
+ android:valueFrom="0.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleX"
+ android:valueFrom="0.0"
+ android:valueTo="1.5"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="316"
+ android:propertyName="scaleX"
+ android:valueFrom="1.5"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="166"
+ android:propertyName="scaleY"
+ android:valueFrom="0.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleY"
+ android:valueFrom="0.0"
+ android:valueTo="1.5"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="316"
+ android:propertyName="scaleY"
+ android:valueFrom="1.5"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/core/res/res/anim/btn_radio_to_on_mtrl_ring_outer_animation.xml b/core/res/res/anim/btn_radio_to_on_mtrl_ring_outer_animation.xml
new file mode 100644
index 0000000..ef07885
--- /dev/null
+++ b/core/res/res/anim/btn_radio_to_on_mtrl_ring_outer_animation.xml
@@ -0,0 +1,67 @@
+<?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.
+-->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="166"
+ android:propertyName="scaleX"
+ android:valueFrom="1.0"
+ android:valueTo="0.5"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleX"
+ android:valueFrom="0.5"
+ android:valueTo="0.9"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="316"
+ android:propertyName="scaleX"
+ android:valueFrom="0.9"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="166"
+ android:propertyName="scaleY"
+ android:valueFrom="1.0"
+ android:valueTo="0.5"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleY"
+ android:valueFrom="0.5"
+ android:valueTo="0.9"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="316"
+ android:propertyName="scaleY"
+ android:valueFrom="0.9"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/core/res/res/anim/btn_radio_to_on_mtrl_ring_outer_path_animation.xml b/core/res/res/anim/btn_radio_to_on_mtrl_ring_outer_path_animation.xml
new file mode 100644
index 0000000..642c004
--- /dev/null
+++ b/core/res/res/anim/btn_radio_to_on_mtrl_ring_outer_path_animation.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="166"
+ android:propertyName="strokeWidth"
+ android:valueFrom="2.0"
+ android:valueTo="18.0"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/btn_radio_to_on_mtrl_animation_interpolator_0" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="strokeWidth"
+ android:valueFrom="18.0"
+ android:valueTo="2.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="316"
+ android:propertyName="strokeWidth"
+ android:valueFrom="2.0"
+ android:valueTo="2.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_000.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_000.png
deleted file mode 100644
index da88e98..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_001.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_001.png
deleted file mode 100644
index 907d92d..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_002.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_002.png
deleted file mode 100644
index 9d24dc1..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_003.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_003.png
deleted file mode 100644
index 8aa2605..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_004.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_004.png
deleted file mode 100644
index b4cdf02..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_005.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_005.png
deleted file mode 100644
index 0724ed7..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_006.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_006.png
deleted file mode 100644
index c9bd4e3..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_007.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_007.png
deleted file mode 100644
index 5630ec3..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_008.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_008.png
deleted file mode 100644
index 4bf666c..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_009.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_009.png
deleted file mode 100644
index dffaa07..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_010.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_010.png
deleted file mode 100644
index 5f86e18..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_011.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_011.png
deleted file mode 100644
index 9b50aef..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_012.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_012.png
deleted file mode 100644
index 1cf5e7f..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_013.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_013.png
deleted file mode 100644
index 2bb641a..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_014.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_014.png
deleted file mode 100644
index 08e7485..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_015.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_015.png
deleted file mode 100644
index 519b5a3..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_000.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index 0d3e1e7..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_001.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_001.png
deleted file mode 100644
index 88c4a9e..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_002.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_002.png
deleted file mode 100644
index 8fa2e88..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_003.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_003.png
deleted file mode 100644
index 53dd9d7..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_004.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_004.png
deleted file mode 100644
index e235195..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_005.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_005.png
deleted file mode 100644
index 1721284..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_006.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_006.png
deleted file mode 100644
index 31819fa..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_007.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_007.png
deleted file mode 100644
index 5de44b9..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_008.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_008.png
deleted file mode 100644
index aa20f65..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_009.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_009.png
deleted file mode 100644
index c379ba7..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_010.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_010.png
deleted file mode 100644
index e23b410..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_011.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_011.png
deleted file mode 100644
index a9543dc..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_012.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_012.png
deleted file mode 100644
index 2473b78..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_013.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_013.png
deleted file mode 100644
index b4acc9c..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_014.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_014.png
deleted file mode 100644
index c9cf344..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_015.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index a8c390e..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_000.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_000.png
deleted file mode 100644
index a2b7fce..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_001.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_001.png
deleted file mode 100644
index fe0d3b1..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_002.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_002.png
deleted file mode 100644
index d66d00d..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_003.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_003.png
deleted file mode 100644
index 2f2f5cd..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_004.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_004.png
deleted file mode 100644
index 72c9495..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_005.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_005.png
deleted file mode 100644
index 7d9090f..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_006.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_006.png
deleted file mode 100644
index c5442e8..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_007.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_007.png
deleted file mode 100644
index ca80cdb..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_008.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_008.png
deleted file mode 100644
index d41a10b..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_009.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_009.png
deleted file mode 100644
index 262c838..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_010.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_010.png
deleted file mode 100644
index 7f6ea8ba..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_011.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_011.png
deleted file mode 100644
index 8d50a81..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_012.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_012.png
deleted file mode 100644
index 0725a68..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_013.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_013.png
deleted file mode 100644
index 6191a4b..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_014.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_014.png
deleted file mode 100644
index 1904d74..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_015.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_015.png
deleted file mode 100644
index bec8dda..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_000.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index 54ef480..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_001.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_001.png
deleted file mode 100644
index 55c5163..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_002.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_002.png
deleted file mode 100644
index 0fe2a897..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_003.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_003.png
deleted file mode 100644
index 86efab7..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_004.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_004.png
deleted file mode 100644
index c0a5ca5..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_005.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_005.png
deleted file mode 100644
index ec55175..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_006.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_006.png
deleted file mode 100644
index 3e4a690..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_007.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_007.png
deleted file mode 100644
index da49734..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_008.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_008.png
deleted file mode 100644
index 471cda1..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_009.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_009.png
deleted file mode 100644
index d560262..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_010.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_010.png
deleted file mode 100644
index f6096b4..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_011.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_011.png
deleted file mode 100644
index 9e2500b..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_012.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_012.png
deleted file mode 100644
index efbac99..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_013.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_013.png
deleted file mode 100644
index 676f0ca..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_014.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_014.png
deleted file mode 100644
index 4803157..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_015.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index 4f8a162..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_000.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_000.png
deleted file mode 100644
index b54c6ff..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_001.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_001.png
deleted file mode 100644
index fff7056..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_002.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_002.png
deleted file mode 100644
index 026462d..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_003.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_003.png
deleted file mode 100644
index 26cc8de..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_004.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_004.png
deleted file mode 100644
index c055fff..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_005.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_005.png
deleted file mode 100644
index a22e780..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_006.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_006.png
deleted file mode 100644
index 357374c..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_007.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_007.png
deleted file mode 100644
index 71d4667..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_008.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_008.png
deleted file mode 100644
index 2ed175e..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_009.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_009.png
deleted file mode 100644
index e0f7d8e..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_010.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_010.png
deleted file mode 100644
index 62b0578..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_011.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_011.png
deleted file mode 100644
index 4d6ef4a..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_012.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_012.png
deleted file mode 100644
index 37cee2d..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_013.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_013.png
deleted file mode 100644
index a8bc25f..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_014.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_014.png
deleted file mode 100644
index cf68d93..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_015.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_015.png
deleted file mode 100644
index 96834bc..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_000.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index d068dbe..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_001.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_001.png
deleted file mode 100644
index 4aabb1e..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_002.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_002.png
deleted file mode 100644
index bbac8e4..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_003.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_003.png
deleted file mode 100644
index 2fc7459..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_004.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_004.png
deleted file mode 100644
index 83c6d0e..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_005.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_005.png
deleted file mode 100644
index 45c08d7..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_006.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_006.png
deleted file mode 100644
index 05b7dfb..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_007.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_007.png
deleted file mode 100644
index baf9964..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_008.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_008.png
deleted file mode 100644
index d6e0369..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_009.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_009.png
deleted file mode 100644
index 3f35270..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_010.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_010.png
deleted file mode 100644
index a5b34dc..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_011.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_011.png
deleted file mode 100644
index 361967b..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_012.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_012.png
deleted file mode 100644
index c478bb7..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_013.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_013.png
deleted file mode 100644
index 075fa0c..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_014.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_014.png
deleted file mode 100644
index d9e364b..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_015.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index 9924496..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_000.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_000.png
deleted file mode 100644
index cbc3833..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_001.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_001.png
deleted file mode 100644
index 4243895..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_002.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_002.png
deleted file mode 100644
index b522d37..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_003.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_003.png
deleted file mode 100644
index 647b965..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_004.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_004.png
deleted file mode 100644
index a317497..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_005.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_005.png
deleted file mode 100644
index 0e4b25f..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_006.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_006.png
deleted file mode 100644
index 6e279d9..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_007.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_007.png
deleted file mode 100644
index f0840cc..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_008.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_008.png
deleted file mode 100644
index 140e9e8..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_009.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_009.png
deleted file mode 100644
index 5cf8ec5..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_010.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_010.png
deleted file mode 100644
index f9624d8..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_011.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_011.png
deleted file mode 100644
index 899df8c..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_012.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_012.png
deleted file mode 100644
index 6543e1c..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_013.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_013.png
deleted file mode 100644
index cd758dd..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_014.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_014.png
deleted file mode 100644
index 72d950c..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_015.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_015.png
deleted file mode 100644
index 07bdbc9..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_000.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index c9af24b..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_001.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_001.png
deleted file mode 100644
index 01de3f5..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_002.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_002.png
deleted file mode 100644
index f428bc5..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_003.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_003.png
deleted file mode 100644
index ab5c008..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_004.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_004.png
deleted file mode 100644
index 5b157cf..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_005.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_005.png
deleted file mode 100644
index 1210be2..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_006.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_006.png
deleted file mode 100644
index e6b4140..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_007.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_007.png
deleted file mode 100644
index b678e08..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_008.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_008.png
deleted file mode 100644
index 6ca2a69..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_009.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_009.png
deleted file mode 100644
index 7de608e..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_010.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_010.png
deleted file mode 100644
index b2bbcce..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_011.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_011.png
deleted file mode 100644
index 6950db3..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_012.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_012.png
deleted file mode 100644
index c790756..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_013.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_013.png
deleted file mode 100644
index ed5d888..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_014.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_014.png
deleted file mode 100644
index 81a4a63..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_015.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index db1d93a..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_000.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_000.png
deleted file mode 100644
index 44028af..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_001.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_001.png
deleted file mode 100644
index ec13a86..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_002.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_002.png
deleted file mode 100644
index 43754eb..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_003.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_003.png
deleted file mode 100644
index 39d1d64..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_004.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_004.png
deleted file mode 100644
index f36f883..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_005.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_005.png
deleted file mode 100644
index 7a4cc5c..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_006.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_006.png
deleted file mode 100644
index 80a21ec..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_007.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_007.png
deleted file mode 100644
index 2141104..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_008.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_008.png
deleted file mode 100644
index 203bd51..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_009.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_009.png
deleted file mode 100644
index 5df6fc5..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_010.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_010.png
deleted file mode 100644
index 6d0fced..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_011.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_011.png
deleted file mode 100644
index 8c0c372..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_012.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_012.png
deleted file mode 100644
index 4fa6f53..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_013.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_013.png
deleted file mode 100644
index d3dbf7d..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_014.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_014.png
deleted file mode 100644
index 4ccf8de..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_015.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_015.png
deleted file mode 100644
index adef871..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_000.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index adef871..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_001.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_001.png
deleted file mode 100644
index 9fc3556..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_002.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_002.png
deleted file mode 100644
index 7f00609..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_003.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_003.png
deleted file mode 100644
index e4aa58d..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_004.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_004.png
deleted file mode 100644
index fe4e4b7..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_005.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_005.png
deleted file mode 100644
index 86666ca..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_006.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_006.png
deleted file mode 100644
index 608faaf..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_007.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_007.png
deleted file mode 100644
index ec95422..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_008.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_008.png
deleted file mode 100644
index 76e2754..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_009.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_009.png
deleted file mode 100644
index 3853eac..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_010.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_010.png
deleted file mode 100644
index 621aff1..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_011.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_011.png
deleted file mode 100644
index d24be2a..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_012.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_012.png
deleted file mode 100644
index df33892..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_013.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_013.png
deleted file mode 100644
index ff4b818..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_014.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_014.png
deleted file mode 100644
index d9793ae..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_015.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index 44028af..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/btn_check_material_anim.xml b/core/res/res/drawable/btn_check_material_anim.xml
index 710a291..e6752b0 100644
--- a/core/res/res/drawable/btn_check_material_anim.xml
+++ b/core/res/res/drawable/btn_check_material_anim.xml
@@ -18,16 +18,16 @@
<item
android:id="@+id/checked"
android:state_checked="true"
- android:drawable="@drawable/ic_checkbox_checked" />
+ android:drawable="@drawable/btn_checkbox_checked_mtrl" />
<item
android:id="@+id/unchecked"
- android:drawable="@drawable/ic_checkbox_unchecked" />
+ android:drawable="@drawable/btn_checkbox_unchecked_mtrl" />
<transition
android:fromId="@+id/unchecked"
android:toId="@+id/checked"
- android:drawable="@drawable/ic_checkbox_unchecked_to_checked_animation" />
+ android:drawable="@drawable/btn_checkbox_unchecked_to_checked_mtrl_animation" />
<transition
android:fromId="@+id/checked"
android:toId="@+id/unchecked"
- android:drawable="@drawable/ic_checkbox_checked_to_unchecked_animation" />
+ android:drawable="@drawable/btn_checkbox_checked_to_unchecked_mtrl_animation" />
</animated-selector>
diff --git a/core/res/res/drawable/ic_checkbox_checked.xml b/core/res/res/drawable/btn_checkbox_checked_mtrl.xml
similarity index 98%
rename from core/res/res/drawable/ic_checkbox_checked.xml
rename to core/res/res/drawable/btn_checkbox_checked_mtrl.xml
index ecde414..e2dacd5 100644
--- a/core/res/res/drawable/ic_checkbox_checked.xml
+++ b/core/res/res/drawable/btn_checkbox_checked_mtrl.xml
@@ -15,7 +15,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:name="ic_checkbox_checked"
+ android:name="btn_checkbox_checked"
android:width="32dp"
android:viewportWidth="48"
android:height="32dp"
diff --git a/core/res/res/drawable/ic_checkbox_checked_to_unchecked_animation.xml b/core/res/res/drawable/btn_checkbox_checked_to_unchecked_mtrl_animation.xml
similarity index 72%
rename from core/res/res/drawable/ic_checkbox_checked_to_unchecked_animation.xml
rename to core/res/res/drawable/btn_checkbox_checked_to_unchecked_mtrl_animation.xml
index fad2233..e6f9edc 100644
--- a/core/res/res/drawable/ic_checkbox_checked_to_unchecked_animation.xml
+++ b/core/res/res/drawable/btn_checkbox_checked_to_unchecked_mtrl_animation.xml
@@ -15,14 +15,14 @@
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_checkbox_checked">
+ android:drawable="@drawable/btn_checkbox_checked_mtrl">
<target
android:name="icon_null"
- android:animation="@anim/ic_checkbox_to_unchecked_icon_null_animation" />
+ android:animation="@anim/btn_checkbox_to_unchecked_icon_null_animation" />
<target
android:name="check_path_merged"
- android:animation="@anim/ic_checkbox_to_unchecked_check_path_merged_animation" />
+ android:animation="@anim/btn_checkbox_to_unchecked_check_path_merged_animation" />
<target
android:name="box_inner_merged"
- android:animation="@anim/ic_checkbox_to_unchecked_box_inner_merged_animation" />
+ android:animation="@anim/btn_checkbox_to_unchecked_box_inner_merged_animation" />
</animated-vector>
diff --git a/core/res/res/drawable/ic_checkbox_unchecked.xml b/core/res/res/drawable/btn_checkbox_unchecked_mtrl.xml
similarity index 98%
rename from core/res/res/drawable/ic_checkbox_unchecked.xml
rename to core/res/res/drawable/btn_checkbox_unchecked_mtrl.xml
index 3329b46..f4c699f 100644
--- a/core/res/res/drawable/ic_checkbox_unchecked.xml
+++ b/core/res/res/drawable/btn_checkbox_unchecked_mtrl.xml
@@ -15,7 +15,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:name="ic_checkbox_unchecked"
+ android:name="btn_checkbox_unchecked"
android:width="32dp"
android:viewportWidth="48"
android:height="32dp"
diff --git a/core/res/res/drawable/ic_checkbox_unchecked_to_checked_animation.xml b/core/res/res/drawable/btn_checkbox_unchecked_to_checked_mtrl_animation.xml
similarity index 73%
rename from core/res/res/drawable/ic_checkbox_unchecked_to_checked_animation.xml
rename to core/res/res/drawable/btn_checkbox_unchecked_to_checked_mtrl_animation.xml
index 68351701..e974e2b 100644
--- a/core/res/res/drawable/ic_checkbox_unchecked_to_checked_animation.xml
+++ b/core/res/res/drawable/btn_checkbox_unchecked_to_checked_mtrl_animation.xml
@@ -15,14 +15,14 @@
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_checkbox_unchecked">
+ android:drawable="@drawable/btn_checkbox_unchecked_mtrl">
<target
android:name="icon_null"
- android:animation="@anim/ic_checkbox_to_checked_icon_null_animation" />
+ android:animation="@anim/btn_checkbox_to_checked_icon_null_animation" />
<target
android:name="box_outer_merged"
- android:animation="@anim/ic_checkbox_to_checked_box_outer_merged_animation" />
+ android:animation="@anim/btn_checkbox_to_checked_box_outer_merged_animation" />
<target
android:name="box_inner_merged"
- android:animation="@anim/ic_checkbox_to_checked_box_inner_merged_animation" />
+ android:animation="@anim/btn_checkbox_to_checked_box_inner_merged_animation" />
</animated-vector>
diff --git a/core/res/res/drawable/btn_radio_material_anim.xml b/core/res/res/drawable/btn_radio_material_anim.xml
index bce579e..941f95d 100644
--- a/core/res/res/drawable/btn_radio_material_anim.xml
+++ b/core/res/res/drawable/btn_radio_material_anim.xml
@@ -15,158 +15,19 @@
-->
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:state_checked="true">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_015"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_enabled="false">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_000"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_checked="true" android:id="@+id/on">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_015"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:id="@+id/off">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_000"
- android:tint="?attr/colorControlNormal" />
- </item>
- <transition android:fromId="@+id/off" android:toId="@+id/on">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_000"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_001"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_002"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_003"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_004"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_005"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_006"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_007"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_008"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_009"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_010"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_011"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_012"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_013"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_014"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_mtrl_015"
- android:tint="?attr/colorControlActivated" />
- </item>
- </animation-list>
- </transition>
- <transition android:fromId="@+id/on" android:toId="@+id/off">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_000"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_001"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_002"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_003"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_004"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_005"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_006"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_007"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_008"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_009"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_010"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_011"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_012"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_013"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_014"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_mtrl_015"
- android:tint="?attr/colorControlNormal" />
- </item>
- </animation-list>
- </transition>
+ <item
+ android:id="@+id/on"
+ android:state_checked="true"
+ android:drawable="@drawable/btn_radio_on_mtrl" />
+ <item
+ android:id="@+id/off"
+ android:drawable="@drawable/btn_radio_off_mtrl" />
+ <transition
+ android:fromId="@+id/on"
+ android:toId="@+id/off"
+ android:drawable="@drawable/btn_radio_on_to_off_mtrl_animation" />
+ <transition
+ android:fromId="@+id/off"
+ android:toId="@+id/on"
+ android:drawable="@drawable/btn_radio_off_to_on_mtrl_animation" />
</animated-selector>
diff --git a/core/res/res/drawable/btn_radio_off_mtrl.xml b/core/res/res/drawable/btn_radio_off_mtrl.xml
new file mode 100644
index 0000000..55c7950
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_off_mtrl.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="btn_radio_to_on_mtrl"
+ android:width="32dp"
+ android:viewportWidth="32"
+ android:height="32dp"
+ android:viewportHeight="32"
+ android:tint="@color/control_checkable_material">
+ <group
+ android:name="btn_radio_to_on_mtrl_0"
+ android:translateX="16"
+ android:translateY="16" >
+ <group
+ android:name="ring_outer" >
+ <path
+ android:name="ring_outer_path"
+ android:strokeColor="#FF000000"
+ android:strokeWidth="2"
+ android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z" />
+ </group>
+ <group
+ android:name="dot_group"
+ android:scaleX="0"
+ android:scaleY="0" >
+ <path
+ android:name="dot_path"
+ android:pathData="M 0.0,-5.0 c -2.7619934082,0.0 -5.0,2.2380065918 -5.0,5.0 c 0.0,2.7619934082 2.2380065918,5.0 5.0,5.0 c 2.7619934082,0.0 5.0,-2.2380065918 5.0,-5.0 c 0.0,-2.7619934082 -2.2380065918,-5.0 -5.0,-5.0 Z"
+ android:fillColor="#FF000000" />
+ </group>
+ </group>
+</vector>
diff --git a/core/res/res/drawable/btn_radio_off_to_on_mtrl_animation.xml b/core/res/res/drawable/btn_radio_off_to_on_mtrl_animation.xml
new file mode 100644
index 0000000..b877c04
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_off_to_on_mtrl_animation.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/btn_radio_off_mtrl" >
+ <target
+ android:name="ring_outer"
+ android:animation="@anim/btn_radio_to_on_mtrl_ring_outer_animation" />
+ <target
+ android:name="ring_outer_path"
+ android:animation="@anim/btn_radio_to_on_mtrl_ring_outer_path_animation" />
+ <target
+ android:name="dot_group"
+ android:animation="@anim/btn_radio_to_on_mtrl_dot_group_animation" />
+</animated-vector>
diff --git a/core/res/res/drawable/btn_radio_on_mtrl.xml b/core/res/res/drawable/btn_radio_on_mtrl.xml
new file mode 100644
index 0000000..ee03dbd
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_on_mtrl.xml
@@ -0,0 +1,45 @@
+<?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.
+-->
+
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="btn_radio_to_off_mtrl"
+ android:width="32dp"
+ android:viewportWidth="32"
+ android:height="32dp"
+ android:viewportHeight="32"
+ android:tint="@color/control_checkable_material">
+ <group
+ android:name="btn_radio_to_off_mtrl_0"
+ android:translateX="16"
+ android:translateY="16" >
+ <group
+ android:name="ring_outer" >
+ <path
+ android:name="ring_outer_path"
+ android:strokeColor="#FF000000"
+ android:strokeWidth="2"
+ android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z" />
+ </group>
+ <group
+ android:name="dot_group" >
+ <path
+ android:name="dot_path"
+ android:pathData="M 0.0,-5.0 c -2.7619934082,0.0 -5.0,2.2380065918 -5.0,5.0 c 0.0,2.7619934082 2.2380065918,5.0 5.0,5.0 c 2.7619934082,0.0 5.0,-2.2380065918 5.0,-5.0 c 0.0,-2.7619934082 -2.2380065918,-5.0 -5.0,-5.0 Z"
+ android:fillColor="#FF000000" />
+ </group>
+ </group>
+</vector>
diff --git a/core/res/res/drawable/btn_radio_on_to_off_mtrl_animation.xml b/core/res/res/drawable/btn_radio_on_to_off_mtrl_animation.xml
new file mode 100644
index 0000000..94ea9e3
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_on_to_off_mtrl_animation.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/btn_radio_on_mtrl" >
+ <target
+ android:name="ring_outer"
+ android:animation="@anim/btn_radio_to_off_mtrl_ring_outer_animation" />
+ <target
+ android:name="ring_outer_path"
+ android:animation="@anim/btn_radio_to_off_mtrl_ring_outer_path_animation" />
+ <target
+ android:name="dot_group"
+ android:animation="@anim/btn_radio_to_off_mtrl_dot_group_animation" />
+</animated-vector>
diff --git a/core/res/res/drawable/ic_arrow_drop_right_black_24dp.xml b/core/res/res/drawable/ic_arrow_drop_right_black_24dp.xml
new file mode 100644
index 0000000..2dd0540
--- /dev/null
+++ b/core/res/res/drawable/ic_arrow_drop_right_black_24dp.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="25.0dp"
+ android:viewportHeight="25.0"
+ android:viewportWidth="24.0"
+ android:width="25.0dp"
+ android:tint="?attr/colorControlNormal"
+ android:autoMirrored="true">
+
+ <group
+ android:name="arrow"
+ android:rotation="90.0"
+ android:pivotX="12.0"
+ android:pivotY="13.0"
+ android:translateY="1.0">
+ <path android:fillColor="#000000" android:pathData="M7,14 L12,9 L17,14 L7,14 Z" />
+ <path android:pathData="M0,0 L24,0 L24,24 L0,24 L0,0 Z" />
+ </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_0.xml b/core/res/res/interpolator/btn_checkbox_checked_mtrl_animation_interpolator_0.xml
similarity index 100%
rename from core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_0.xml
rename to core/res/res/interpolator/btn_checkbox_checked_mtrl_animation_interpolator_0.xml
diff --git a/core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_1.xml b/core/res/res/interpolator/btn_checkbox_checked_mtrl_animation_interpolator_1.xml
similarity index 100%
rename from core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_1.xml
rename to core/res/res/interpolator/btn_checkbox_checked_mtrl_animation_interpolator_1.xml
diff --git a/core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_0.xml b/core/res/res/interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_0.xml
similarity index 100%
copy from core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_0.xml
copy to core/res/res/interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_0.xml
diff --git a/core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_1.xml b/core/res/res/interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_1.xml
similarity index 100%
copy from core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_1.xml
copy to core/res/res/interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_1.xml
diff --git a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_0.xml b/core/res/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml
similarity index 85%
rename from core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_0.xml
rename to core/res/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml
index ceac663..97c9373 100644
--- a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_0.xml
+++ b/core/res/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 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.
@@ -17,4 +16,4 @@
<pathInterpolator
xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0.0,0.0 l 1.0,0.0 l 0.0,1.0" />
+ android:pathData="M 0.0,0.0 c 0.4,0.0 0.4,1.0 1.0,1.0" />
diff --git a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_0.xml b/core/res/res/interpolator/btn_radio_to_on_mtrl_animation_interpolator_0.xml
similarity index 85%
copy from core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_0.xml
copy to core/res/res/interpolator/btn_radio_to_on_mtrl_animation_interpolator_0.xml
index ceac663..97c9373 100644
--- a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_0.xml
+++ b/core/res/res/interpolator/btn_radio_to_on_mtrl_animation_interpolator_0.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 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.
@@ -17,4 +16,4 @@
<pathInterpolator
xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0.0,0.0 l 1.0,0.0 l 0.0,1.0" />
+ android:pathData="M 0.0,0.0 c 0.4,0.0 0.4,1.0 1.0,1.0" />
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/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml
index 0bc636f..8b8c93a 100644
--- a/core/res/res/layout/popup_menu_item_layout.xml
+++ b/core/res/res/layout/popup_menu_item_layout.xml
@@ -57,6 +57,15 @@
</RelativeLayout>
+ <ImageView
+ android:id="@+id/submenuarrow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="8dp"
+ android:scaleType="center"
+ android:visibility="gone" />
+
<!-- Checkbox, and/or radio button will be inserted here. -->
</com.android.internal.view.menu.ListMenuItemView>
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-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index dde02d5..2d847ee 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -1149,7 +1149,7 @@
<string name="sync_really_delete" msgid="2572600103122596243">"আইটেমগুলি মুছুন"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"মোছাগুলিকে পূর্বাবস্থায় ফেরান"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"এখন কার মতো কিছু করবেন না"</string>
- <string name="choose_account_label" msgid="5655203089746423927">"একটি অ্যাকাউন্ট নির্বাচন করুন"</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"একটি অ্যাকাউন্ট বাছুন"</string>
<string name="add_account_label" msgid="2935267344849993553">"একটি অ্যাকাউন্ট যোগ করুন"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"অ্যাকাউন্ট যোগ করুন"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"বাড়ান"</string>
diff --git a/core/res/res/values-ca-watch/strings.xml b/core/res/res/values-ca-watch/strings.xml
index b01ca63..231450f 100644
--- a/core/res/res/values-ca-watch/strings.xml
+++ b/core/res/res/values-ca-watch/strings.xml
@@ -31,7 +31,7 @@
<string name="permgrouplab_camerawear" msgid="4543951283103407017">"fer fotos i enregistrar vídeos"</string>
<string name="permgrouplab_phonewear" msgid="134365036753766126">"fer i gestionar trucades telefòniques"</string>
<string name="permgrouplab_sensorswear" msgid="1429324744329327663">"accedir a les dades del sensor sobre els signes vitals"</string>
- <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"ser la barra d\'estat"</string>
+ <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"aparèixer a la barra d\'estat"</string>
<string name="permlab_bodySensorswear" msgid="7857941041202791873">"accedir als sensors corporals (com ara monitors de freqüència cardíaca)"</string>
<string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"accedir a la ubicació precisa (basada en el GPS i la xarxa)"</string>
<string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"accedir a la ubicació aproximada (basada en la xarxa)"</string>
@@ -40,7 +40,7 @@
<string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"gestionar els propietaris del perfil i del dispositiu"</string>
<string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"canviar l\'estat de WiMAX"</string>
<string name="permlab_handoverStatuswear" msgid="4835786819716499249">"rebre l\'estat de la transferència d\'Android Beam"</string>
- <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"indicar la ruta de sortida del contingut multimèdia"</string>
+ <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"indicar la sortida del fitxer multimèdia"</string>
<string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"llegir les sessions d\'instal·lació"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"sol·licitar els paquets d\'instal·lació"</string>
+ <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"sol·licitar la instal·lació de paquets"</string>
</resources>
diff --git a/core/res/res/values-el-watch/strings.xml b/core/res/res/values-el-watch/strings.xml
index 8081013..83d9c3d 100644
--- a/core/res/res/values-el-watch/strings.xml
+++ b/core/res/res/values-el-watch/strings.xml
@@ -42,5 +42,5 @@
<string name="permlab_handoverStatuswear" msgid="4835786819716499249">"λήψη κατάστασης μεταφοράς Android Beam"</string>
<string name="permlab_route_media_outputwear" msgid="8737024341474587192">"δρομολόγηση εξόδου μέσων"</string>
<string name="permlab_readInstallSessionswear" msgid="9059478058685861989">"ανάγνωση περιόδων σύνδεσης εγκατάστασης"</string>
- <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"αίτημα εγκατάστασης πακέτων."</string>
+ <string name="permlab_requestInstallPackageswear" msgid="4982025836783539503">"αίτημα εγκατάστασης πακέτων"</string>
</resources>
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-watch/strings.xml b/core/res/res/values-eu-rES-watch/strings.xml
index 011105e..0fca702 100644
--- a/core/res/res/values-eu-rES-watch/strings.xml
+++ b/core/res/res/values-eu-rES-watch/strings.xml
@@ -26,7 +26,7 @@
<string name="permgrouplab_locationwear" msgid="6275317222482780209">"Atzitu erlojuaren kokapena"</string>
<string name="permgrouplab_calendarwear" msgid="441900844045065081">"Atzitu egutegia"</string>
<string name="permgrouplab_smswear" msgid="6849506550342974220">"Bidali eta ikusi SMS mezuak"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"Atzitu erlojuko argazkiak, multimedia-elementuak eta fitxategiak"</string>
+ <string name="permgrouplab_storagewear" msgid="1003807594193602313">"Atzitu erlojuko argazkiak, multimedia-edukia eta fitxategiak"</string>
<string name="permgrouplab_microphonewear" msgid="1047561180980891136">"Grabatu audioa"</string>
<string name="permgrouplab_camerawear" msgid="4543951283103407017">"Atera argazkiak eta grabatu bideoak"</string>
<string name="permgrouplab_phonewear" msgid="134365036753766126">"Egin eta kudeatu telefono-deiak"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 6422355..6c03159 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -775,7 +775,7 @@
<string name="autofill_area" msgid="3547409050889952423">"منطقه"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"امارات"</string>
<string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"خواندن سابقه و نشانکهای وب شما"</string>
- <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"به برنامه اجازه میدهد سابقه نشانیهای اینترنتی را که مرورگر بازدید کرده است و همه نشانکهای مرورگر را بخواند. توجه: این مجوز توسط مرورگرهای شخص ثالث یا سایر برنامههای دارای قابلیت مرور وب قابل اجرا نیست."</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"به برنامه اجازه میدهد سابقه نشانیهای وب را که مرورگر بازدید کرده است و همه نشانکهای مرورگر را بخواند. توجه: این مجوز توسط مرورگرهای شخص ثالث یا سایر برنامههای دارای قابلیت مرور وب قابل اجرا نیست."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"نوشتن نشانکهای وب و سابقه"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"به برنامه اجازه میدهد سابقه مرورگر یا نشانکهای ذخیره شده در رایانهٔ لوحی شما را اصلاح کند. این ویژگی ممکن است به برنامه اجازه دهد دادههای مرورگر را حذف یا اصلاح کند. توجه: این مجوز ممکن است توسط مرورگرهای شخص ثالث یا سایر برنامههای دارای قابلیت مرور وب قابل اجرا نباشد."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"به برنامه اجازه میدهد تا سابقه یا نشانکهای ذخیره شده مرورگر در تلویزیون شما را تغییر دهد. شاید به برنامه اجازه دهد تا دادههای «مرورگر» را پاک کند یا تغییر دهد. توجه: این مجوز شاید توسط مرورگرهای شخص ثالث یا سایر برنامهها با قابلیتهای مرور وب اجرا شود."</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-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 50adefa..15536cf 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1405,7 +1405,7 @@
<string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"Code PIN actuel"</string>
<string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"Nouveau code PIN"</string>
<string name="restr_pin_confirm_pin" msgid="8501523829633146239">"Confirmer le nouveau code PIN"</string>
- <string name="restr_pin_create_pin" msgid="8017600000263450337">"Créer un code PIN pour modifier les restrictions"</string>
+ <string name="restr_pin_create_pin" msgid="8017600000263450337">"Créer un code pour modifier les restrictions"</string>
<string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"Les codes PIN ne correspondent pas. Veuillez réessayer."</string>
<string name="restr_pin_error_too_short" msgid="8173982756265777792">"Le code PIN est trop court. Il doit comporter au moins 4 chiffres."</string>
<plurals name="restr_pin_countdown" formatted="false" msgid="9061246974881224688">
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 9979d0f..7b5ca29 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -951,7 +951,7 @@
<string name="volume_icon_description_media" msgid="4217311719665194215">"Volume dos elementos multimedia"</string>
<string name="volume_icon_description_notification" msgid="7044986546477282274">"Volume das notificacións"</string>
<string name="ringtone_default" msgid="3789758980357696936">"Ton de chamada predeterminado"</string>
- <string name="ringtone_default_with_actual" msgid="8129563480895990372">"Ton de chamada predeterminado(<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+ <string name="ringtone_default_with_actual" msgid="8129563480895990372">"Ton de chamada predeterminado (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
<string name="ringtone_silent" msgid="7937634392408977062">"Ningún"</string>
<string name="ringtone_picker_title" msgid="3515143939175119094">"Tons de chamada"</string>
<string name="ringtone_unknown" msgid="5477919988701784788">"Ton de chamada descoñecido"</string>
diff --git a/core/res/res/values-mcc204-mnc12/config.xml b/core/res/res/values-mcc204-mnc12/config.xml
new file mode 100644
index 0000000..80432d7
--- /dev/null
+++ b/core/res/res/values-mcc204-mnc12/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>20408</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc260-nl/strings.xml b/core/res/res/values-mcc310-mnc260-nl/strings.xml
index 1c6b892..ac4961c 100644
--- a/core/res/res/values-mcc310-mnc260-nl/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-nl/strings.xml
@@ -23,10 +23,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="7239039348648848288">"Als u wilt bellen en berichten wilt verzenden via wifi, moet u eerst uw provider vragen deze service in te stellen. Schakel bellen via wifi vervolgens opnieuw in via \'Instellingen\'."</item>
+ <item msgid="7239039348648848288">"Als je wilt bellen en berichten wilt verzenden via wifi, moet je eerst je provider vragen deze service in te stellen. Schakel bellen via wifi vervolgens opnieuw in via \'Instellingen\'."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
- <item msgid="483847327467331298">"Registreren bij uw provider"</item>
+ <item msgid="483847327467331298">"Registreren bij je provider"</item>
</string-array>
<string name="wfcSpnFormat" msgid="4982938551498609442">"Bellen via wifi van %s"</string>
</resources>
diff --git a/core/res/res/values-mcc450-mnc06/config.xml b/core/res/res/values-mcc450-mnc06/config.xml
index 63f9823..819c2a5 100644
--- a/core/res/res/values-mcc450-mnc06/config.xml
+++ b/core/res/res/values-mcc450-mnc06/config.xml
@@ -25,4 +25,7 @@
-->
<integer name="config_mobile_mtu">1428</integer>
+ <!-- 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-mcc450-mnc08/config.xml b/core/res/res/values-mcc450-mnc08/config.xml
index 950c62b..ca26ec1 100644
--- a/core/res/res/values-mcc450-mnc08/config.xml
+++ b/core/res/res/values-mcc450-mnc08/config.xml
@@ -25,4 +25,7 @@
-->
<integer name="config_mobile_mtu">1450</integer>
+ <!-- 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-mcc730-mnc01/config.xml b/core/res/res/values-mcc730-mnc01/config.xml
new file mode 100644
index 0000000..22f4027
--- /dev/null
+++ b/core/res/res/values-mcc730-mnc01/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>73010</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc730-mnc07/config.xml b/core/res/res/values-mcc730-mnc07/config.xml
new file mode 100644
index 0000000..836ddf9
--- /dev/null
+++ b/core/res/res/values-mcc730-mnc07/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>73002</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc730-mnc08/config.xml b/core/res/res/values-mcc730-mnc08/config.xml
new file mode 100644
index 0000000..836ddf9
--- /dev/null
+++ b/core/res/res/values-mcc730-mnc08/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>73002</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc730-mnc10/config.xml b/core/res/res/values-mcc730-mnc10/config.xml
new file mode 100644
index 0000000..58b7d78
--- /dev/null
+++ b/core/res/res/values-mcc730-mnc10/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>73001</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 43264a7..2bdc9c8 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1149,7 +1149,7 @@
<string name="sync_really_delete" msgid="2572600103122596243">"ഇനങ്ങൾ ഇല്ലാതാക്കുക"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"ഇല്ലാതാക്കിയവ പഴയപടിയാക്കുക"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"ഇപ്പോൾ ഒന്നും ചെയ്യേണ്ടതില്ല"</string>
- <string name="choose_account_label" msgid="5655203089746423927">"ഒരു അക്കൗണ്ട് തിരഞ്ഞെടുക്കുക"</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"അക്കൗണ്ട് തിരഞ്ഞെടുക്കൂ"</string>
<string name="add_account_label" msgid="2935267344849993553">"ഒരു അക്കൗണ്ട് ചേർക്കുക"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"അക്കൗണ്ട് ചേർക്കുക"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"വർദ്ധിപ്പിക്കുക"</string>
diff --git a/core/res/res/values-ne-rNP-watch/strings.xml b/core/res/res/values-ne-rNP-watch/strings.xml
index a38de99..1b4ffc9 100644
--- a/core/res/res/values-ne-rNP-watch/strings.xml
+++ b/core/res/res/values-ne-rNP-watch/strings.xml
@@ -26,7 +26,7 @@
<string name="permgrouplab_locationwear" msgid="6275317222482780209">"यो घडीको स्थान पहुँच गर्नुहोस्"</string>
<string name="permgrouplab_calendarwear" msgid="441900844045065081">"आफ्नो पात्रोमा पहुँच गर्नुहोस्"</string>
<string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"आफ्नो समयमा फोटो, मिडिया, र फाइलहरू हेर्नुहोस्"</string>
+ <string name="permgrouplab_storagewear" msgid="1003807594193602313">"आफ्नो समयमा तस्बिर, मिडिया, र फाइलहरू हेर्नुहोस्"</string>
<string name="permgrouplab_microphonewear" msgid="1047561180980891136">"अडियो रेकर्ड गर्नुहोस्"</string>
<string name="permgrouplab_camerawear" msgid="4543951283103407017">"तस्बिरहरू खिच्नुहोस् र भिडियो रेकर्ड गर्नुहोस्"</string>
<string name="permgrouplab_phonewear" msgid="134365036753766126">"फोन कलहरू गर्नुहोस् र व्यवस्थापन गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl-watch/strings.xml b/core/res/res/values-nl-watch/strings.xml
index 590d146..967cf90 100644
--- a/core/res/res/values-nl-watch/strings.xml
+++ b/core/res/res/values-nl-watch/strings.xml
@@ -22,15 +22,15 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> van <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"Sensoren"</string>
- <string name="permgrouplab_contactswear" msgid="2340286500790908344">"toegang tot uw contacten"</string>
+ <string name="permgrouplab_contactswear" msgid="2340286500790908344">"toegang tot je contacten"</string>
<string name="permgrouplab_locationwear" msgid="6275317222482780209">"toegang tot de locatie van dit horloge"</string>
- <string name="permgrouplab_calendarwear" msgid="441900844045065081">"toegang tot uw agenda"</string>
+ <string name="permgrouplab_calendarwear" msgid="441900844045065081">"toegang tot je agenda"</string>
<string name="permgrouplab_smswear" msgid="6849506550342974220">"sms\'jes verzenden en bekijken"</string>
- <string name="permgrouplab_storagewear" msgid="1003807594193602313">"toegang tot foto\'s, media en bestanden op uw horloge"</string>
+ <string name="permgrouplab_storagewear" msgid="1003807594193602313">"toegang tot foto\'s, media en bestanden op je horloge"</string>
<string name="permgrouplab_microphonewear" msgid="1047561180980891136">"audio opnemen"</string>
<string name="permgrouplab_camerawear" msgid="4543951283103407017">"foto\'s maken en video opnemen"</string>
<string name="permgrouplab_phonewear" msgid="134365036753766126">"bellen en telefoontjes beheren"</string>
- <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"toegang tot sensorgegevens over uw vitale functies"</string>
+ <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"toegang tot sensorgegevens over je vitale functies"</string>
<string name="permlab_statusBarServicewear" msgid="2469402818964691034">"de statusbalk zijn"</string>
<string name="permlab_bodySensorswear" msgid="7857941041202791873">"toegang tot lichaamssensoren (zoals hartslagmeters)"</string>
<string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"toegang tot precieze locatie (GPS- en netwerkgebaseerd)"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index d77dca1f..a850943 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -53,17 +53,17 @@
<string name="serviceErased" msgid="1288584695297200972">"Wissen uitgevoerd."</string>
<string name="passwordIncorrect" msgid="7612208839450128715">"Onjuist wachtwoord."</string>
<string name="mmiComplete" msgid="8232527495411698359">"MMI voltooid."</string>
- <string name="badPin" msgid="9015277645546710014">"De oude pincode die u heeft ingevoerd, is onjuist."</string>
- <string name="badPuk" msgid="5487257647081132201">"De PUK-code die u heeft ingevoerd, is onjuist."</string>
- <string name="mismatchPin" msgid="609379054496863419">"De pincodes die u heeft ingevoerd, komen niet overeen."</string>
+ <string name="badPin" msgid="9015277645546710014">"De oude pincode die je hebt ingevoerd, is onjuist."</string>
+ <string name="badPuk" msgid="5487257647081132201">"De PUK-code die je hebt ingevoerd, is onjuist."</string>
+ <string name="mismatchPin" msgid="609379054496863419">"De pincodes die je hebt ingevoerd, komen niet overeen."</string>
<string name="invalidPin" msgid="3850018445187475377">"Voer een pincode van 4 tot 8 cijfers in."</string>
<string name="invalidPuk" msgid="8761456210898036513">"Typ een PUK-code die 8 cijfers of langer is."</string>
- <string name="needPuk" msgid="919668385956251611">"Uw SIM-kaart is vergrendeld met de PUK-code. Typ de PUK-code om te ontgrendelen."</string>
+ <string name="needPuk" msgid="919668385956251611">"Je SIM-kaart is vergrendeld met de PUK-code. Typ de PUK-code om te ontgrendelen."</string>
<string name="needPuk2" msgid="4526033371987193070">"Voer de PUK2-code in om de SIM-kaart te ontgrendelen."</string>
<string name="enablePin" msgid="209412020907207950">"Mislukt. Schakel SIM/RUIM-vergrendeling in."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
- <item quantity="other">U heeft nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over voordat de simkaart wordt vergrendeld.</item>
- <item quantity="one">U heeft nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat de simkaart wordt vergrendeld.</item>
+ <item quantity="other">Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over voordat de simkaart wordt vergrendeld.</item>
+ <item quantity="one">Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat de simkaart wordt vergrendeld.</item>
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
@@ -167,14 +167,14 @@
<string name="low_memory" product="default" msgid="3475999286680000541">"Telefoongeheugen is vol. Verwijder enkele bestanden om ruimte vrij te maken."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Netwerk kan worden gecontroleerd"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Door een onbekende derde partij"</string>
- <string name="ssl_ca_cert_noti_by_administrator" msgid="550758088185764312">"Door uw werkprofielbeheerder"</string>
+ <string name="ssl_ca_cert_noti_by_administrator" msgid="550758088185764312">"Door je werkprofielbeheerder"</string>
<string name="ssl_ca_cert_noti_managed" msgid="4030263497686867141">"Door <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
<string name="work_profile_deleted" msgid="5005572078641980632">"Werkprofiel verwijderd"</string>
<string name="work_profile_deleted_description" msgid="6305147513054341102">"Werkprofiel verwijderd wegens ontbrekende beheerapp."</string>
- <string name="work_profile_deleted_details" msgid="226615743462361248">"De beheerapp van het werkprofiel ontbreekt of is beschadigd. Als gevolg hiervan zijn uw werkprofiel en alle gerelateerde gegevens verwijderd. Neem voor hulp contact op met uw beheerder."</string>
- <string name="work_profile_deleted_description_dpm_wipe" msgid="6019770344820507579">"Uw werkprofiel is niet meer beschikbaar op dit apparaat."</string>
- <string name="factory_reset_warning" msgid="5423253125642394387">"Uw apparaat wordt gewist"</string>
- <string name="factory_reset_message" msgid="4905025204141900666">"Er ontbreken onderdelen van de beheerapp of de app is beschadigd, waardoor de app niet kan worden gebruikt. Uw apparaat wordt nu gewist. Neem voor hulp contact op met uw beheerder."</string>
+ <string name="work_profile_deleted_details" msgid="226615743462361248">"De beheerapp van het werkprofiel ontbreekt of is beschadigd. Als gevolg hiervan zijn je werkprofiel en alle gerelateerde gegevens verwijderd. Neem voor hulp contact op met je beheerder."</string>
+ <string name="work_profile_deleted_description_dpm_wipe" msgid="6019770344820507579">"Je werkprofiel is niet meer beschikbaar op dit apparaat."</string>
+ <string name="factory_reset_warning" msgid="5423253125642394387">"Je apparaat wordt gewist"</string>
+ <string name="factory_reset_message" msgid="4905025204141900666">"Er ontbreken onderdelen van de beheerapp of de app is beschadigd, waardoor de app niet kan worden gebruikt. Je apparaat wordt nu gewist. Neem voor hulp contact op met je beheerder."</string>
<string name="me" msgid="6545696007631404292">"Ik"</string>
<string name="power_dialog" product="tablet" msgid="8545351420865202853">"Tabletopties"</string>
<string name="power_dialog" product="tv" msgid="6153888706430556356">"TV-opties"</string>
@@ -194,10 +194,10 @@
<string name="reboot_to_reset_title" msgid="4142355915340627490">"Terugzetten op fabrieksinstellingen"</string>
<string name="reboot_to_reset_message" msgid="2432077491101416345">"Opnieuw opstarten…"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Uitschakelen..."</string>
- <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Uw tablet wordt uitgeschakeld."</string>
- <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"Uw tv wordt uitgeschakeld.."</string>
- <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Uw horloge wordt uitgeschakeld."</string>
- <string name="shutdown_confirm" product="default" msgid="649792175242821353">"Uw telefoon wordt uitgeschakeld."</string>
+ <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Je tablet wordt uitgeschakeld."</string>
+ <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"Je tv wordt uitgeschakeld.."</string>
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Je horloge wordt uitgeschakeld."</string>
+ <string name="shutdown_confirm" product="default" msgid="649792175242821353">"Je telefoon wordt uitgeschakeld."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Wilt u afsluiten?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Opnieuw opstarten in veilige modus"</string>
<string name="reboot_safemode_confirm" msgid="55293944502784668">"Wilt u opnieuw opstarten in de veilige modus? Als u dit doet, worden alle geïnstalleerde applicaties van derden uitgeschakeld. Ze worden weer ingeschakeld als u weer opnieuw opstart."</string>
@@ -210,7 +210,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Uitschakelen"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Foutenrapport"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Foutenrapport genereren"</string>
- <string name="bugreport_message" msgid="398447048750350456">"Hiermee worden gegevens over de huidige status van uw apparaat verzameld en als e-mail verzonden. Wanneer u een foutenrapport start, duurt het even voordat het kan worden verzonden. Even geduld alstublieft."</string>
+ <string name="bugreport_message" msgid="398447048750350456">"Hiermee worden gegevens over de huidige status van je apparaat verzameld en als e-mail verzonden. Wanneer u een foutenrapport start, duurt het even voordat het kan worden verzonden. Even geduld alstublieft."</string>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Stille modus"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Geluid is UIT"</string>
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Geluid is AAN"</string>
@@ -227,15 +227,15 @@
<string name="user_owner_label" msgid="2804351898001038951">"Persoonlijk"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"Werk"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacten"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"toegang krijgen tot uw contacten"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"toegang krijgen tot je contacten"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Locatie"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"de locatie van dit apparaat openen"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"toegang krijgen tot uw agenda"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"toegang krijgen tot je agenda"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"Sms"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"sms\'jes verzenden en bekijken"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Opslagruimte"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"toegang krijgen tot foto\'s, media en bestanden op uw apparaat"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"toegang krijgen tot foto\'s, media en bestanden op je apparaat"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfoon"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"audio opnemen"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
@@ -243,9 +243,9 @@
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefoon"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"bellen en telefoontjes beheren"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Lichaamssensoren"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"toegang tot sensorgegevens over uw vitale functies"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"toegang tot sensorgegevens over je vitale functies"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Inhoud van vensters ophalen"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"De inhoud inspecteren van een venster waarmee u interactie heeft."</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"De inhoud inspecteren van een venster waarmee je interactie hebt."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"\'Verkennen via aanraking\' inschakelen"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="5800552516779249356">"Aangeraakte items worden hardop benoemd en het scherm kan worden verkend door middel van aanraking."</string>
<string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"Verbeterde internettoegankelijkheid inschakelen"</string>
@@ -265,33 +265,33 @@
<string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"uitgaande oproepen doorschakelen"</string>
<string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"De app toestaan het nummer te bekijken dat wordt gekozen voor een uitgaande oproep, met de mogelijkheid de oproep om te leiden naar een ander nummer of de oproep helemaal af te breken."</string>
<string name="permlab_receiveSms" msgid="8673471768947895082">"tekstberichten (SMS) ontvangen"</string>
- <string name="permdesc_receiveSms" msgid="6424387754228766939">"Hiermee kan de app sms-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar uw apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
+ <string name="permdesc_receiveSms" msgid="6424387754228766939">"Hiermee kan de app sms-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar je apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"tekstberichten (MMS) ontvangen"</string>
- <string name="permdesc_receiveMms" msgid="533019437263212260">"Hiermee kan de app MMS-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar uw apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
+ <string name="permdesc_receiveMms" msgid="533019437263212260">"Hiermee kan de app MMS-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar je apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"infodienstberichten lezen"</string>
- <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Toestaan dat de app infodienstberichten leest die worden ontvangen op uw apparaat. Infodienstberichten worden verzonden naar bepaalde locaties om u te waarschuwen voor noodsituaties. Schadelijke apps kunnen de prestaties of verwerking van uw apparaat verstoren wanneer een infodienstbericht wordt ontvangen."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Toestaan dat de app infodienstberichten leest die worden ontvangen op je apparaat. Infodienstberichten worden verzonden naar bepaalde locaties om u te waarschjeen voor noodsituaties. Schadelijke apps kunnen de prestaties of verwerking van je apparaat verstoren wanneer een infodienstbericht wordt ontvangen."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"Geabonneerde feeds lezen"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Hiermee kan de app details over de huidige gesynchroniseerde feeds achterhalen."</string>
<string name="permlab_sendSms" msgid="7544599214260982981">"sms\'jes verzenden en bekijken"</string>
- <string name="permdesc_sendSms" msgid="7094729298204937667">"Hiermee kan de app sms-berichten verzenden. Dit kan tot onverwachte kosten leiden. Schadelijke apps kunnen u geld kosten doordat ze zonder uw bevestiging berichten kunnen verzenden."</string>
- <string name="permlab_readSms" msgid="8745086572213270480">"uw tekstberichten (SMS of MMS) lezen"</string>
- <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Hiermee kan de app sms-berichten lezen die zijn opgeslagen op uw tablet of simkaart. De app kan alle sms-berichten lezen, ongeacht inhoud of vertrouwelijkheid."</string>
- <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Hiermee kan de app sms-berichten lezen die zijn opgeslagen op uw tv of simkaart. De app kan alle sms-berichten lezen, ongeacht inhoud of vertrouwelijkheid."</string>
- <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Hiermee kan de app sms-berichten lezen die zijn opgeslagen op uw telefoon of simkaart. De app kan alle sms-berichten lezen, ongeacht inhoud of vertrouwelijkheid."</string>
+ <string name="permdesc_sendSms" msgid="7094729298204937667">"Hiermee kan de app sms-berichten verzenden. Dit kan tot onverwachte kosten leiden. Schadelijke apps kunnen u geld kosten doordat ze zonder je bevestiging berichten kunnen verzenden."</string>
+ <string name="permlab_readSms" msgid="8745086572213270480">"je tekstberichten (SMS of MMS) lezen"</string>
+ <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Hiermee kan de app sms-berichten lezen die zijn opgeslagen op je tablet of simkaart. De app kan alle sms-berichten lezen, ongeacht inhoud of vertrouwelijkheid."</string>
+ <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Hiermee kan de app sms-berichten lezen die zijn opgeslagen op je tv of simkaart. De app kan alle sms-berichten lezen, ongeacht inhoud of vertrouwelijkheid."</string>
+ <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Hiermee kan de app sms-berichten lezen die zijn opgeslagen op je telefoon of simkaart. De app kan alle sms-berichten lezen, ongeacht inhoud of vertrouwelijkheid."</string>
<string name="permlab_receiveWapPush" msgid="5991398711936590410">"tekstberichten (WAP) ontvangen"</string>
- <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Hiermee kan de app WAP-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar uw apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
+ <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Hiermee kan de app WAP-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar je apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"actieve apps ophalen"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Hiermee kan de app informatie ophalen over actieve en recent uitgevoerde taken. Zo kan de app informatie vinden over welke apps op het apparaat worden gebruikt."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"Profiel- en apparaateigenaren beheren"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Apps toestaan de profieleigenaren en apparaateigenaar in te stellen."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"actieve apps opnieuw rangschikken"</string>
- <string name="permdesc_reorderTasks" msgid="7734217754877439351">"Hiermee kan de app taken naar de voor- en achtergrond verplaatsen. De app kan dit doen zonder om uw bevestiging te vragen."</string>
+ <string name="permdesc_reorderTasks" msgid="7734217754877439351">"Hiermee kan de app taken naar de voor- en achtergrond verplaatsen. De app kan dit doen zonder om je bevestiging te vragen."</string>
<string name="permlab_enableCarMode" msgid="5684504058192921098">"automodus inschakelen"</string>
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"Hiermee kan de app de automodus inschakelen."</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"andere apps sluiten"</string>
<string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Hiermee kan de app achtergrondprocessen van andere apps beëindigen. Hierdoor kunnen andere apps worden gestopt."</string>
<string name="permlab_systemAlertWindow" msgid="3543347980839518613">"Weergeven over andere apps"</string>
- <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Hiermee kan de app tekenen op andere apps of de gebruikersinterface. De app kan uw gebruik van de interface in alle apps verstoren, of wijzigen wat u in andere apps denkt te zien."</string>
+ <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Hiermee kan de app tekenen op andere apps of de gebruikersinterface. De app kan je gebruik van de interface in alle apps verstoren, of wijzigen wat u in andere apps denkt te zien."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"app altijd laten uitvoeren"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Hiermee kan de app gedeelten van zichzelf persistent maken in het geheugen. Dit kan de hoeveelheid geheugen beperken die beschikbaar is voor andere apps, waardoor de tablet trager kan worden."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Hiermee kan de app gedeelten van zichzelf persistent maken in het geheugen. Dit kan de hoeveelheid geheugen beperken die beschikbaar is voor andere apps, waardoor de tv trager kan worden."</string>
@@ -299,7 +299,7 @@
<string name="permlab_getPackageSize" msgid="7472921768357981986">"opslagruimte van app meten"</string>
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"Hiermee kan de app de bijbehorende code, gegevens en cachegrootten ophalen."</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"systeeminstellingen aanpassen"</string>
- <string name="permdesc_writeSettings" msgid="7775723441558907181">"Hiermee kan de app de instellingsgegevens van het systeem aanpassen. Schadelijke apps kunnen de configuratie van uw systeem verstoren."</string>
+ <string name="permdesc_writeSettings" msgid="7775723441558907181">"Hiermee kan de app de instellingsgegevens van het systeem aanpassen. Schadelijke apps kunnen de configuratie van je systeem verstoren."</string>
<string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"uitvoeren bij opstarten"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"Hiermee kan de app zichzelf laten starten zodra het systeem is opgestart. Hierdoor kan het langer duren voordat de tablet is opgestart en een app kan altijd actief zijn, wat de tablet kan vertragen."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4525890122209673621">"Hiermee kan de app zichzelf laten starten zodra het systeem is opgestart. Hierdoor kan het langer duren voordat de tv is opgestart en een app kan altijd actief zijn, wat de tablet kan vertragen."</string>
@@ -308,54 +308,54 @@
<string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Hiermee kan de app sticky broadcasts verzenden die behouden blijven nadat de broadcast is beëindigd. Bij overmatig gebruik kan de tablet traag of instabiel worden omdat er te veel geheugenruimte wordt gebruikt."</string>
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Hiermee kan de app sticky broadcasts verzenden die achterblijven nadat de uitzending is afgelopen. Overmatig gebruik kan de tv traag instabiel maken doordat er te veel geheugen wordt gebruikt."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Hiermee kan de app sticky broadcasts verzenden die behouden blijven nadat de broadcast is beëindigd. Bij overmatig gebruik kan de telefoon traag of instabiel worden omdat er te veel geheugenruimte wordt gebruikt."</string>
- <string name="permlab_readContacts" msgid="8348481131899886131">"uw contacten lezen"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Hiermee kan de app gegevens lezen over de contacten die zijn opgeslagen op uw tablet, inclusief de frequentie waarmee u heeft gebeld, gemaild of op andere manieren heeft gecommuniceerd met specifieke personen. Met deze toestemming kunnen apps uw contactgegevens opslaan, en schadelijke apps kunnen zonder uw medeweten contactgegevens delen."</string>
- <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Hiermee kan de app gegevens lezen over de contacten die zijn opgeslagen op uw tv, inclusief de frequentie waarmee u heeft gebeld, gemaild of op andere manieren heeft gecommuniceerd met specifieke personen. Met deze toestemming kunnen apps uw contactgegevens opslaan, en schadelijke apps kunnen zonder uw medeweten contactgegevens delen."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Hiermee kan de app gegevens lezen over de contacten die zijn opgeslagen op uw telefoon, inclusief de frequentie waarmee u heeft gebeld, gemaild of op andere manieren heeft gecommuniceerd met specifieke personen. Met deze toestemming kunnen apps uw contactgegevens opslaan, en schadelijke apps kunnen zonder uw medeweten contactgegevens delen."</string>
- <string name="permlab_writeContacts" msgid="5107492086416793544">"uw contacten aanpassen"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Hiermee kan de app gegevens wijzigen over de contacten die zijn opgeslagen op uw tablet, inclusief de frequentie waarmee u heeft gebeld, gemaild of op andere manieren heeft gecommuniceerd met specifieke contacten. Met deze toestemming kunnen apps contactgegevens verwijderen."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Hiermee kan de app gegevens wijzigen over de contacten die zijn opgeslagen op uw tv, inclusief de frequentie waarmee u heeft gebeld, gemaild of op andere manieren heeft gecommuniceerd met specifieke contacten. Met deze toestemming kunnen apps contactgegevens verwijderen."</string>
- <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Hiermee kan de app gegevens wijzigen over de contacten die zijn opgeslagen op uw telefoon, inclusief de frequentie waarmee u heeft gebeld, gemaild of op andere manieren heeft gecommuniceerd met specifieke contacten. Met deze toestemming kunnen apps contactgegevens verwijderen."</string>
+ <string name="permlab_readContacts" msgid="8348481131899886131">"je contacten lezen"</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Hiermee kan de app gegevens lezen over de contacten die zijn opgeslagen op je tablet, inclusief de frequentie waarmee je hebt gebeld, gemaild of op andere manieren hebt gecommuniceerd met specifieke personen. Met deze toestemming kunnen apps je contactgegevens opslaan, en schadelijke apps kunnen zonder je medeweten contactgegevens delen."</string>
+ <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Hiermee kan de app gegevens lezen over de contacten die zijn opgeslagen op je tv, inclusief de frequentie waarmee je hebt gebeld, gemaild of op andere manieren hebt gecommuniceerd met specifieke personen. Met deze toestemming kunnen apps je contactgegevens opslaan, en schadelijke apps kunnen zonder je medeweten contactgegevens delen."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Hiermee kan de app gegevens lezen over de contacten die zijn opgeslagen op je telefoon, inclusief de frequentie waarmee je hebt gebeld, gemaild of op andere manieren hebt gecommuniceerd met specifieke personen. Met deze toestemming kunnen apps je contactgegevens opslaan, en schadelijke apps kunnen zonder je medeweten contactgegevens delen."</string>
+ <string name="permlab_writeContacts" msgid="5107492086416793544">"je contacten aanpassen"</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Hiermee kan de app gegevens wijzigen over de contacten die zijn opgeslagen op je tablet, inclusief de frequentie waarmee je hebt gebeld, gemaild of op andere manieren hebt gecommuniceerd met specifieke contacten. Met deze toestemming kunnen apps contactgegevens verwijderen."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Hiermee kan de app gegevens wijzigen over de contacten die zijn opgeslagen op je tv, inclusief de frequentie waarmee je hebt gebeld, gemaild of op andere manieren hebt gecommuniceerd met specifieke contacten. Met deze toestemming kunnen apps contactgegevens verwijderen."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Hiermee kan de app gegevens wijzigen over de contacten die zijn opgeslagen op je telefoon, inclusief de frequentie waarmee je hebt gebeld, gemaild of op andere manieren hebt gecommuniceerd met specifieke contacten. Met deze toestemming kunnen apps contactgegevens verwijderen."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"gesprekkenlijst lezen"</string>
- <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Hiermee kan de app het gesprekkenlijst van uw tablet lezen, inclusief gegevens over inkomende en uitgaande oproepen. Met deze toestemming kunnen apps uw oproeploggegevens opslaan, en schadelijke apps kunnen logoproepgegevens zonder uw medeweten delen."</string>
- <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Hiermee kan de app het gesprekkenlijst van uw tv lezen, inclusief gegevens over inkomende en uitgaande oproepen. Met deze toestemming kunnen apps uw oproeploggegevens opslaan, en schadelijke apps kunnen oproeploggegevens zonder uw medeweten delen."</string>
- <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Hiermee kan de app het gesprekkenlijst van uw telefoon lezen, inclusief gegevens over inkomende en uitgaande oproepen. Met deze toestemming kunnen apps uw oproeploggegevens opslaan, en schadelijke apps kunnen logoproepgegevens zonder uw medeweten delen."</string>
+ <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Hiermee kan de app het gesprekkenlijst van je tablet lezen, inclusief gegevens over inkomende en uitgaande oproepen. Met deze toestemming kunnen apps je oproeploggegevens opslaan, en schadelijke apps kunnen logoproepgegevens zonder je medeweten delen."</string>
+ <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Hiermee kan de app het gesprekkenlijst van je tv lezen, inclusief gegevens over inkomende en uitgaande oproepen. Met deze toestemming kunnen apps je oproeploggegevens opslaan, en schadelijke apps kunnen oproeploggegevens zonder je medeweten delen."</string>
+ <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Hiermee kan de app het gesprekkenlijst van je telefoon lezen, inclusief gegevens over inkomende en uitgaande oproepen. Met deze toestemming kunnen apps je oproeploggegevens opslaan, en schadelijke apps kunnen logoproepgegevens zonder je medeweten delen."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"gesprekkenlijst schrijven"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Toestaan dat de app het gesprekkenlijst van uw tablet aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee uw gesprekkenlijst wissen of aanpassen."</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Toestaan dat de app het gesprekkenlijst van uw tv aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee uw gesprekkenlijst wissen of aanpassen."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Toestaan dat de app het gesprekkenlijst van uw telefoon aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee uw gesprekkenlijst wissen of aanpassen."</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Toestaan dat de app het gesprekkenlijst van je tablet aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Toestaan dat de app het gesprekkenlijst van je tv aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Toestaan dat de app het gesprekkenlijst van je telefoon aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
<string name="permlab_bodySensors" msgid="4871091374767171066">"lichaamssensoren (zoals hartslagmeters)"</string>
- <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Hiermee kan de app toegang krijgen tot gegevens van sensoren die uw lichamelijke conditie controleren, zoals uw hartslag."</string>
+ <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Hiermee kan de app toegang krijgen tot gegevens van sensoren die je lichamelijke conditie controleren, zoals je hartslag."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"agenda-afspraken en vertrouwelijke informatie lezen"</string>
- <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Hiermee kan de app alle agenda-afspraken lezen die zijn opgeslagen op uw tablet, inclusief die van vrienden of collega\'s. De app kan uw agenda delen of uw agendagegevens opslaan, ongeacht vertrouwelijkheid of gevoeligheid."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Hiermee kan de app alle agenda-afspraken lezen die zijn opgeslagen op uw tv, inclusief die van vrienden of collega\'s. De app kan uw agenda delen of uw agendagegevens opslaan, ongeacht vertrouwelijkheid of gevoeligheid."</string>
- <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Hiermee kan de app alle agenda-afspraken lezen die zijn opgeslagen op uw telefoon, inclusief die van vrienden of collega\'s. De app kan uw agenda delen of uw agendagegevens opslaan, ongeacht vertrouwelijkheid of gevoeligheid."</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Hiermee kan de app alle agenda-afspraken lezen die zijn opgeslagen op je tablet, inclusief die van vrienden of collega\'s. De app kan je agenda delen of je agendagegevens opslaan, ongeacht vertrouwelijkheid of gevoeligheid."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Hiermee kan de app alle agenda-afspraken lezen die zijn opgeslagen op je tv, inclusief die van vrienden of collega\'s. De app kan je agenda delen of je agendagegevens opslaan, ongeacht vertrouwelijkheid of gevoeligheid."</string>
+ <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Hiermee kan de app alle agenda-afspraken lezen die zijn opgeslagen op je telefoon, inclusief die van vrienden of collega\'s. De app kan je agenda delen of je agendagegevens opslaan, ongeacht vertrouwelijkheid of gevoeligheid."</string>
<string name="permlab_writeCalendar" msgid="8438874755193825647">"agenda-afspraken toevoegen of wijzigen en e-mails verzenden aan gasten zonder medeweten van de eigenaren"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Hiermee kan de app afspraken toevoegen, verwijderen en wijzigen die u kunt bewerken op uw tablet, inclusief afspraken van vrienden of collega\'s. Zo kan de app berichten verzenden die afkomstig lijken te zijn van agenda-eigenaren, of afspraken aanpassen zonder medeweten van de eigenaar."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Hiermee kan de app afspraken toevoegen, verwijderen en wijzigen die u op uw tv kunt aanpassen, inclusief afspraken van vrienden of collega\'s. Met deze toestemming zou de app berichten kunnen verzenden die afkomstig lijken te zijn van agenda-eigenaren of afspraken kunnen aanpassen zonder medeweten van de eigenaar."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Hiermee kan de app afspraken toevoegen, verwijderen en wijzigen die u kunt bewerken op uw telefoon, inclusief afspraken van vrienden of collega\'s. Zo kan de app berichten verzenden die afkomstig lijken te zijn van agenda-eigenaren, of afspraken aanpassen zonder medeweten van de eigenaar."</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Hiermee kan de app afspraken toevoegen, verwijderen en wijzigen die u kunt bewerken op je tablet, inclusief afspraken van vrienden of collega\'s. Zo kan de app berichten verzenden die afkomstig lijken te zijn van agenda-eigenaren, of afspraken aanpassen zonder medeweten van de eigenaar."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Hiermee kan de app afspraken toevoegen, verwijderen en wijzigen die u op je tv kunt aanpassen, inclusief afspraken van vrienden of collega\'s. Met deze toestemming zou de app berichten kunnen verzenden die afkomstig lijken te zijn van agenda-eigenaren of afspraken kunnen aanpassen zonder medeweten van de eigenaar."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Hiermee kan de app afspraken toevoegen, verwijderen en wijzigen die u kunt bewerken op je telefoon, inclusief afspraken van vrienden of collega\'s. Zo kan de app berichten verzenden die afkomstig lijken te zijn van agenda-eigenaren, of afspraken aanpassen zonder medeweten van de eigenaar."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"toegang tot extra opdrachten van locatieaanbieder"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Hiermee kan de app toegang krijgen tot extra opdrachten voor de locatieprovider. De app kan hiermee de werking van GPS of andere locatiebronnen te verstoren."</string>
<string name="permlab_accessFineLocation" msgid="1191898061965273372">"precieze locatie (GPS- en netwerkgebaseerd)"</string>
- <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Hiermee kan de app uw precieze locatie bepalen via GPS (Global Positioning System) of netwerklocatiebronnen zoals zendmasten en wifi. Deze locatieservices moeten zijn ingeschakeld en beschikbaar zijn op uw apparaat voordat de app ze kan gebruiken. Apps kunnen dit gebruiken om te bepalen waar u bent en verbruiken mogelijk extra acculading."</string>
+ <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Hiermee kan de app je precieze locatie bepalen via GPS (Global Positioning System) of netwerklocatiebronnen zoals zendmasten en wifi. Deze locatieservices moeten zijn ingeschakeld en beschikbaar zijn op je apparaat voordat de app ze kan gebruiken. Apps kunnen dit gebruiken om te bepalen waar u bent en verbruiken mogelijk extra acculading."</string>
<string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"geschatte locatie (netwerkgebaseerd)"</string>
- <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Hiermee kan de app beschikken over uw geschatte locatie. Deze locatie wordt afgeleid van locatieservices die netwerklocatiebronnen zoals zendmasten en wifi gebruiken. Deze locatieservices moeten zijn ingeschakeld en beschikbaar zijn op uw apparaat voordat de app ze kan gebruiken. Apps kunnen dit gebruiken om ongeveer te bepalen waar u zich bevindt."</string>
- <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"uw audio-instellingen wijzigen"</string>
+ <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Hiermee kan de app beschikken over je geschatte locatie. Deze locatie wordt afgeleid van locatieservices die netwerklocatiebronnen zoals zendmasten en wifi gebruiken. Deze locatieservices moeten zijn ingeschakeld en beschikbaar zijn op je apparaat voordat de app ze kan gebruiken. Apps kunnen dit gebruiken om ongeveer te bepalen waar u zich bevindt."</string>
+ <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"je audio-instellingen wijzigen"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Hiermee kan de app algemene audio-instellingen wijzigen zoals het volume en welke luidspreker wordt gebruikt voor de uitvoer."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"audio opnemen"</string>
- <string name="permdesc_recordAudio" msgid="4906839301087980680">"Hiermee kan de app audio opnemen met de microfoon. Met deze toestemming kan de app op elk moment audio opnemen, zonder om uw bevestiging te vragen."</string>
+ <string name="permdesc_recordAudio" msgid="4906839301087980680">"Hiermee kan de app audio opnemen met de microfoon. Met deze toestemming kan de app op elk moment audio opnemen, zonder om je bevestiging te vragen."</string>
<string name="permlab_sim_communication" msgid="1180265879464893029">"sim-communicatie"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Hiermee kan de app opdrachten verzenden naar de simkaart. Dit is erg gevaarlijk."</string>
<string name="permlab_camera" msgid="3616391919559751192">"foto\'s en video\'s maken"</string>
- <string name="permdesc_camera" msgid="8497216524735535009">"Hiermee kan de app foto\'s en video\'s maken met de camera. Met deze toestemming kan de app de camera altijd gebruiken, zonder uw bevestiging."</string>
+ <string name="permdesc_camera" msgid="8497216524735535009">"Hiermee kan de app foto\'s en video\'s maken met de camera. Met deze toestemming kan de app de camera altijd gebruiken, zonder je bevestiging."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"trilling beheren"</string>
<string name="permdesc_vibrate" msgid="6284989245902300945">"Hiermee kan de app de trilstand beheren."</string>
<string name="permlab_flashlight" msgid="2155920810121984215">"zaklamp bedienen"</string>
<string name="permdesc_flashlight" msgid="6522284794568368310">"Hiermee kan de app de zaklamp bedienen."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"telefoonnummers rechtstreeks bellen"</string>
- <string name="permdesc_callPhone" msgid="3740797576113760827">"Hiermee kan de app zonder uw tussenkomst telefoonnummers bellen. Dit kan tot onverwachte kosten of oproepen leiden. De app kan hiermee geen noodnummers bellen. Schadelijke apps kunnen u geld kosten door nummers te bellen zonder om uw bevestiging te vragen."</string>
+ <string name="permdesc_callPhone" msgid="3740797576113760827">"Hiermee kan de app zonder je tussenkomst telefoonnummers bellen. Dit kan tot onverwachte kosten of oproepen leiden. De app kan hiermee geen noodnummers bellen. Schadelijke apps kunnen u geld kosten door nummers te bellen zonder om je bevestiging te vragen."</string>
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"toegang tot IMS-service voor bellen"</string>
- <string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Hiermee kan de app de IMS-service gebruiken om te bellen zonder uw tussenkomst."</string>
+ <string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Hiermee kan de app de IMS-service gebruiken om te bellen zonder je tussenkomst."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"telefoonstatus en -identiteit lezen"</string>
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Hiermee kan de app toegang krijgen tot de telefoonfuncties van het apparaat, Met deze toestemming kan de app het telefoonnummer en de apparaat-ID\'s bepalen, of een oproep actief is, en het andere telefoonnummer waarmee wordt gebeld."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"voorkomen dat tablet overschakelt naar slaapmodus"</string>
@@ -370,16 +370,16 @@
<string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"Hiermee kan de app de infraroodzender van de telefoon gebruiken."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"achtergrond instellen"</string>
<string name="permdesc_setWallpaper" msgid="7373447920977624745">"Hiermee kan de app de systeemachtergrond instellen."</string>
- <string name="permlab_setWallpaperHints" msgid="3278608165977736538">"uw achtergrondformaat aanpassen"</string>
+ <string name="permlab_setWallpaperHints" msgid="3278608165977736538">"je achtergrondformaat aanpassen"</string>
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"Hiermee kan de app de grootte van de achtergrond instellen."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"tijdzone instellen"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"Hiermee kan de app de tijdzone van de tablet wijzigen."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Hiermee kan de app de tijdzone van de tv wijzigen."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"Hiermee kan de app de tijdzone van de telefoon wijzigen."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"accounts op het apparaat vinden"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Hiermee krijgt de app toegang tot de lijst met accounts die op de tablet bekend zijn. Dit kunnen ook accounts zijn die zijn gemaakt door apps die u heeft geïnstalleerd."</string>
- <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Hiermee krijgt de app toegang tot de lijst met accounts die op de tv bekend zijn. Dit kunnen ook accounts zijn die zijn gemaakt door apps die u heeft geïnstalleerd."</string>
- <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Hiermee krijgt de app toegang tot de lijst met accounts die op de telefoon bekend zijn. Dit kunnen ook accounts zijn die zijn gemaakt door apps die u heeft geïnstalleerd."</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Hiermee krijgt de app toegang tot de lijst met accounts die op de tablet bekend zijn. Dit kunnen ook accounts zijn die zijn gemaakt door apps die je hebt geïnstalleerd."</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Hiermee krijgt de app toegang tot de lijst met accounts die op de tv bekend zijn. Dit kunnen ook accounts zijn die zijn gemaakt door apps die je hebt geïnstalleerd."</string>
+ <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Hiermee krijgt de app toegang tot de lijst met accounts die op de telefoon bekend zijn. Dit kunnen ook accounts zijn die zijn gemaakt door apps die je hebt geïnstalleerd."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"netwerkverbindingen weergeven"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Hiermee kan de app informatie bekijken over netwerkverbindingen, zoals welke netwerken er zijn en welke verbonden zijn."</string>
<string name="permlab_createNetworkSockets" msgid="8018758136404323658">"volledige netwerktoegang"</string>
@@ -393,9 +393,9 @@
<string name="permlab_changeWifiState" msgid="6550641188749128035">"Wifi-verbinding maken en verbreken"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"Hiermee kan de app zich koppelen aan en ontkoppelen van wifi-toegangspunten en wijzigingen aanbrengen in de apparaatconfiguratie voor wifi-netwerken."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"Wifi Multicast-ontvangst toestaan"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar uw tablet. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Hiermee kan de app pakketten ontvangen die zijn verzonden naar alle apparaten op een Wi-Fi-netwerk met multicastadressen en niet alleen uw tv. Er wordt meer stroom verbruikt dan in de niet-multicastmodus."</string>
- <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar uw telefoon. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar je tablet. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Hiermee kan de app pakketten ontvangen die zijn verzonden naar alle apparaten op een Wi-Fi-netwerk met multicastadressen en niet alleen je tv. Er wordt meer stroom verbruikt dan in de niet-multicastmodus."</string>
+ <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar je telefoon. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"Bluetooth-instellingen openen"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Hiermee kan de app de lokale Bluetooth-tablet configureren en externe apparaten zoeken en koppelen."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Hiermee kan de app de configuratie van de lokale Bluetooth-tv weergeven en externe apparaten zoeken en een koppeling maken."</string>
@@ -412,7 +412,7 @@
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Hiermee kan de app de Bluetooth-configuratie van de telefoon bekijken en verbindingen met gekoppelde apparaten maken en accepteren."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"Near Field Communication regelen"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"Hiermee kan de app communiceren met NFC-tags (Near Field Communication), kaarten en lezers."</string>
- <string name="permlab_disableKeyguard" msgid="3598496301486439258">"uw schermvergrendeling uitschakelen"</string>
+ <string name="permlab_disableKeyguard" msgid="3598496301486439258">"je schermvergrendeling uitschakelen"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Hiermee kan de app de toetsenblokkering en bijbehorende wachtwoordbeveiliging uitschakelen. Zo kan de telefoon de toetsenblokkering uitschakelen wanneer er een oproep binnenkomt en de toetsenblokkering weer inschakelen als de oproep is beëindigd."</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"Vingerafdrukhardware beheren"</string>
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Hiermee kan de app methoden aanroepen om vingerafdruksjablonen toe te voegen en te verwijderen voor gebruik."</string>
@@ -441,12 +441,12 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Hiermee kan een app de synchronisatie-instellingen aanpassen voor een account. Deze toestemming kan bijvoorbeeld worden gebruikt om synchronisatie van de app Personen in te schakelen voor een account."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"synchronisatiestatistieken lezen"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Hiermee kan een app de synchronisatiestatistieken voor een account lezen, inclusief de geschiedenis van synchronisatie-activiteiten en hoeveel gegevens zijn gesynchroniseerd."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"de inhoud van uw USB-opslag lezen"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"de inhoud van uw SD-kaart lezen"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"De app toestaan de inhoud van uw USB-opslag te lezen."</string>
- <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"De app toestaan de inhoud van uw SD-kaart te lezen."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"de inhoud van uw USB-opslag aanpassen of verwijderen"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"de inhoud van uw SD-kaart aanpassen of verwijderen"</string>
+ <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"de inhoud van je USB-opslag lezen"</string>
+ <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"de inhoud van je SD-kaart lezen"</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"De app toestaan de inhoud van je USB-opslag te lezen."</string>
+ <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"De app toestaan de inhoud van je SD-kaart te lezen."</string>
+ <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"de inhoud van je USB-opslag aanpassen of verwijderen"</string>
+ <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"de inhoud van je SD-kaart aanpassen of verwijderen"</string>
<string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Hiermee kan de app schrijven naar de USB-opslag."</string>
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Hiermee kan de app schrijven naar de SD-kaart."</string>
<string name="permlab_use_sip" msgid="2052499390128979920">"SIP-oproepen plaatsen/ontvangen"</string>
@@ -668,7 +668,7 @@
<string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Plaats een simkaart."</string>
<string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"De simkaart ontbreekt of kan niet worden gelezen. Plaats een simkaart."</string>
<string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Onbruikbare simkaart."</string>
- <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"Uw simkaart is permanent uitgeschakeld.\n Neem contact op met uw mobiele serviceprovider voor een nieuwe simkaart."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"Je simkaart is permanent uitgeschakeld.\n Neem contact op met je mobiele serviceprovider voor een nieuwe simkaart."</string>
<string name="lockscreen_transport_prev_description" msgid="6300840251218161534">"Vorig nummer"</string>
<string name="lockscreen_transport_next_description" msgid="573285210424377338">"Volgend nummer"</string>
<string name="lockscreen_transport_pause_description" msgid="3980308465056173363">"Onderbreken"</string>
@@ -682,28 +682,28 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Raadpleeg de gebruikershandleiding of neem contact op met de klantenservice."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kaart is vergrendeld."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-kaart ontgrendelen..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"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="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"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="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"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="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"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 met uw aanmeldingsgegevens voor Google.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt u gevraagd uw tv te ontgrendelen met uw inloggegevens voor Google.\n\n Probeer het opnieuw over <xliff:g id="NUMBER_2">%d</xliff:g> seconden."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"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 met uw aanmeldingsgegevens voor Google.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"U heeft op onjuiste wijze <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt de tv hersteld naar de fabriekswaarden en gaan alle gebruikersgegevens verloren."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"U heeft nu <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"U heeft <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de tablet."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"U heeft op onjuiste wijze <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. De tv wordt nu hersteld naar de fabrieksinstellingen."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"U heeft <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de telefoon."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"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="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"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="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"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="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"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 met je aanmeldingsgegevens voor Google.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt je gevraagd je tv te ontgrendelen met je inloggegevens voor Google.\n\n Probeer het opnieuw over <xliff:g id="NUMBER_2">%d</xliff:g> seconden."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"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 met je aanmeldingsgegevens voor Google.\n\n Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Je hebt op onjuiste wijze <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt de tv hersteld naar de fabriekswaarden en gaan alle gebruikersgegevens verloren."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Je hebt nu <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de tablet."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Je hebt op onjuiste wijze <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. De tv wordt nu hersteld naar de fabrieksinstellingen."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de telefoon."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Probeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Patroon vergeten?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Account ontgrendelen"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"Te veel patroonpogingen"</string>
- <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"Log in op uw Google-account om te ontgrendelen."</string>
+ <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"Log in op je Google-account om te ontgrendelen."</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Gebruikersnaam (e-mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Wachtwoord"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Inloggen"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Gebruikersnaam of wachtwoord ongeldig."</string>
- <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"Bent u uw gebruikersnaam of wachtwoord vergeten?\nGa naar "<b>"https://www.google.com/accounts/recovery"</b>"."</string>
+ <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"Bent u je gebruikersnaam of wachtwoord vergeten?\nGa naar "<b>"https://www.google.com/accounts/recovery"</b>"."</string>
<string name="lockscreen_glogin_checking_password" msgid="7114627351286933867">"Controleren…"</string>
<string name="lockscreen_unlock_label" msgid="737440483220667054">"Ontgrendelen"</string>
<string name="lockscreen_sound_on_label" msgid="9068877576513425970">"Geluid aan"</string>
@@ -774,23 +774,23 @@
<string name="autofill_parish" msgid="8202206105468820057">"Gemeente"</string>
<string name="autofill_area" msgid="3547409050889952423">"Gebied"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"Emiraat"</string>
- <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"uw webbladwijzers en -geschiedenis lezen"</string>
+ <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"je webbladwijzers en -geschiedenis lezen"</string>
<string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Hiermee kan de app de geschiedenis lezen van alle URL\'s die in de systeemeigen browser zijn bezocht, en alle bladwijzers in de systeemeigen browser. Let op: deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"webbladwijzers en -geschiedenis schrijven"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bladwijzers die zijn opgeslagen op uw tablet. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden.."</string>
- <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bladwijzers die zijn opgeslagen op uw tv. De app kan browsergegevens wissen of aanpassen. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bladwijzers die zijn opgeslagen op uw telefoon. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bladwijzers die zijn opgeslagen op je tablet. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden.."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bladwijzers die zijn opgeslagen op je tv. De app kan browsergegevens wissen of aanpassen. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bladwijzers die zijn opgeslagen op je telefoon. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"een alarm instellen"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Hiermee kan de app een alarm instellen in een geïnstalleerde wekkerapp. Deze functie wordt door sommige wekkerapps niet geïmplementeerd."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"voicemail toevoegen"</string>
- <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Hiermee kan de app berichten toevoegen aan de inbox van uw voicemail."</string>
+ <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Hiermee kan de app berichten toevoegen aan de inbox van je voicemail."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"geolocatiemachtigingen voor browser aanpassen"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Hiermee kan de app de geolocatiemachtigingen van de browser aanpassen. Schadelijke apps kunnen dit gebruiken om locatiegegevens te verzenden naar willekeurige websites."</string>
<string name="save_password_message" msgid="767344687139195790">"Wilt u dat de browser dit wachtwoord onthoudt?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Niet nu"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Onthouden"</string>
<string name="save_password_never" msgid="8274330296785855105">"Nooit"</string>
- <string name="open_permission_deny" msgid="7374036708316629800">"U heeft geen toestemming om deze pagina te openen."</string>
+ <string name="open_permission_deny" msgid="7374036708316629800">"Je hebt geen toestemming om deze pagina te openen."</string>
<string name="text_copied" msgid="4985729524670131385">"Tekst naar klembord gekopieerd."</string>
<string name="more_item_label" msgid="4650918923083320495">"Meer"</string>
<string name="prepend_shortcut_label" msgid="2572214461676015642">"Menu+"</string>
@@ -805,8 +805,8 @@
<string name="searchview_description_submit" msgid="2688450133297983542">"Zoekopdracht verzenden"</string>
<string name="searchview_description_voice" msgid="2453203695674994440">"Gesproken zoekopdrachten"</string>
<string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"\'Verkennen via aanraking\' aan?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> wil \'Verkennen via aanraking\' inschakelen. Wanneer \'Verkennen via aanraking\' is ingeschakeld, kunt u beschrijvingen beluisteren of bekijken van wat er onder uw vinger staat of aanraakbewerkingen uitvoeren op de tablet."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> wil \'Verkennen via aanraking\' inschakelen. Wanneer \'Verkennen via aanraking\' is ingeschakeld, kunt u beschrijvingen beluisteren of bekijken van wat er onder uw vinger staat of aanraakbewerkingen uitvoeren op de telefoon."</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> wil \'Verkennen via aanraking\' inschakelen. Wanneer \'Verkennen via aanraking\' is ingeschakeld, kunt u beschrijvingen beluisteren of bekijken van wat er onder je vinger staat of aanraakbewerkingen uitvoeren op de tablet."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> wil \'Verkennen via aanraking\' inschakelen. Wanneer \'Verkennen via aanraking\' is ingeschakeld, kunt u beschrijvingen beluisteren of bekijken van wat er onder je vinger staat of aanraakbewerkingen uitvoeren op de telefoon."</string>
<string name="oneMonthDurationPast" msgid="7396384508953779925">"1 maand geleden"</string>
<string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Meer dan 1 maand geleden"</string>
<plurals name="last_num_days" formatted="false" msgid="5104533550723932025">
@@ -868,7 +868,7 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstacties"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Opslagruimte is bijna vol"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bepaalde systeemfuncties werken mogelijk niet"</string>
- <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat u 250 MB vrije ruimte heeft en start opnieuw."</string>
+ <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."</string>
<string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> wordt uitgevoerd"</string>
<string name="app_running_notification_text" msgid="4653586947747330058">"Raak aan voor meer informatie of om de app te stoppen."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -996,8 +996,8 @@
<string name="sms_control_yes" msgid="3663725993855816807">"Toestaan"</string>
<string name="sms_control_no" msgid="625438561395534982">"Weigeren"</string>
<string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> wil graag een bericht verzenden naar <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string>
- <string name="sms_short_code_details" msgid="5873295990846059400">"Hiervoor "<b>"worden mogelijk kosten in rekening gebracht"</b>" op uw mobiele account."</string>
- <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Hiervoor worden kosten in rekening gebracht op uw mobiele account."</b></string>
+ <string name="sms_short_code_details" msgid="5873295990846059400">"Hiervoor "<b>"worden mogelijk kosten in rekening gebracht"</b>" op je mobiele account."</string>
+ <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Hiervoor worden kosten in rekening gebracht op je mobiele account."</b></string>
<string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Verzenden"</string>
<string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"Annuleren"</string>
<string name="sms_short_code_remember_choice" msgid="5289538592272218136">"Mijn keuze onthouden"</string>
@@ -1008,7 +1008,7 @@
<string name="sim_removed_message" msgid="5450336489923274918">"Het mobiele netwerk is pas beschikbaar zodra u het apparaat opnieuw start met een geldige simkaart."</string>
<string name="sim_done_button" msgid="827949989369963775">"Gereed"</string>
<string name="sim_added_title" msgid="3719670512889674693">"Simkaart aangesloten"</string>
- <string name="sim_added_message" msgid="7797975656153714319">"Start uw apparaat opnieuw voor toegang tot het mobiele netwerk."</string>
+ <string name="sim_added_message" msgid="7797975656153714319">"Start je apparaat opnieuw voor toegang tot het mobiele netwerk."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"Opnieuw starten"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"Tijd instellen"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"Datum instellen"</string>
@@ -1090,15 +1090,15 @@
<string name="ime_action_default" msgid="2840921885558045721">"Uitvoeren"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Nummer bellen\nmet <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Contact maken\nmet <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"De volgende apps verzoeken om toegang tot uw account, nu en in de toekomst."</string>
+ <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"De volgende apps verzoeken om toegang tot je account, nu en in de toekomst."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Wilt u dit verzoek toestaan?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"Verzoek om toegang"</string>
<string name="allow" msgid="7225948811296386551">"Toestaan"</string>
<string name="deny" msgid="2081879885755434506">"Weigeren"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"Toestemming gevraagd"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"Toestemming gevraagd\nvoor account <xliff:g id="ACCOUNT">%s</xliff:g>."</string>
- <string name="forward_intent_to_owner" msgid="1207197447013960896">"U gebruikt deze app buiten uw werkprofiel"</string>
- <string name="forward_intent_to_work" msgid="621480743856004612">"U gebruikt deze app in uw werkprofiel"</string>
+ <string name="forward_intent_to_owner" msgid="1207197447013960896">"U gebruikt deze app buiten je werkprofiel"</string>
+ <string name="forward_intent_to_work" msgid="621480743856004612">"U gebruikt deze app in je werkprofiel"</string>
<string name="input_method_binding_label" msgid="1283557179944992649">"Invoermethode"</string>
<string name="sync_binding_label" msgid="3687969138375092423">"Synchroniseren"</string>
<string name="accessibility_binding_label" msgid="4148120742096474641">"Toegankelijkheid"</string>
@@ -1264,7 +1264,7 @@
<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">%1$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_pin_instructions" msgid="2377242233495111557">"Pincode opgeven"</string>
<string name="kg_password_instructions" msgid="5753646556186936819">"Wachtwoord invoeren"</string>
@@ -1278,28 +1278,28 @@
<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_login_instructions" msgid="1100551261265506448">"Als u wilt ontgrendelen, moet u inloggen op uw Google-account."</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Als u wilt ontgrendelen, moet u inloggen op je Google-account."</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"Gebruikersnaam (e-mail)"</string>
<string name="kg_login_password_hint" msgid="9057289103827298549">"Wachtwoord"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"Inloggen"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Ongeldige gebruikersnaam of wachtwoord."</string>
- <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bent u uw gebruikersnaam of wachtwoord vergeten?\nGa naar "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bent u je gebruikersnaam of wachtwoord vergeten?\nGa naar "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="1052685197710252395">"Account controleren…"</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="1575557200627128949">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"U heeft op onjuiste wijze <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt de tv hersteld naar de fabriekswaarden en gaan alle gebruikersgegevens verloren."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"U heeft nu <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"U heeft <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de tablet."</string>
- <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"U heeft op onjuiste wijze <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. De tv wordt nu hersteld naar de fabrieksinstellingen."</string>
- <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"U heeft <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de telefoon."</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="tv" msgid="4224651132862313471">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt u gevraagd uw tv te ontgrendelen met een e-mailaccount.\n\n Probeer het opnieuw over <xliff:g id="NUMBER_2">%d</xliff:g> seconden."</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_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="1575557200627128949">"Je hebt <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Je hebt op onjuiste wijze <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt de tv hersteld naar de fabriekswaarden en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Je hebt nu <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de tablet."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Je hebt op onjuiste wijze <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tv te ontgrendelen. De tv wordt nu hersteld naar de fabrieksinstellingen."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Je hebt <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de telefoon."</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="tv" msgid="4224651132862313471">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onjuiste pogingen, wordt je gevraagd je tv te ontgrendelen met een e-mailaccount.\n\n Probeer het opnieuw over <xliff:g id="NUMBER_2">%d</xliff:g> seconden."</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_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwijderen"</string>
- <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls u langere tijd op hoog volume naar muziek luistert, raakt uw gehoor mogelijk beschadigd."</string>
+ <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls u langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
<string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Blijf het scherm met twee vingers aanraken om toegankelijkheid in te schakelen."</string>
<string name="accessibility_enabled" msgid="1381972048564547685">"Toegankelijkheid ingeschakeld."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toegankelijkheid geannuleerd."</string>
@@ -1307,7 +1307,7 @@
<string name="user_switching_message" msgid="2871009331809089783">"Overschakelen naar <xliff:g id="NAME">%1$s</xliff:g>…"</string>
<string name="owner_name" msgid="2716755460376028154">"Eigenaar"</string>
<string name="error_message_title" msgid="4510373083082500195">"Fout"</string>
- <string name="error_message_change_not_allowed" msgid="1347282344200417578">"Deze wijziging is niet toegestaan door uw beheerder"</string>
+ <string name="error_message_change_not_allowed" msgid="1347282344200417578">"Deze wijziging is niet toegestaan door je beheerder"</string>
<string name="app_not_found" msgid="3429141853498927379">"Er is geen app gevonden om deze actie uit te voeren"</string>
<string name="revoke" msgid="5404479185228271586">"Intrekken"</string>
<string name="mediasize_iso_a0" msgid="1994474252931294172">"ISO A0"</string>
@@ -1433,10 +1433,10 @@
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Vraag pin voor losmaken"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Vraag patroon voor losmaken"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Vraag wachtwoord voor losmaken"</string>
- <string name="package_installed_device_owner" msgid="8420696545959087545">"Geïnstalleerd door uw beheerder"</string>
- <string name="package_updated_device_owner" msgid="8856631322440187071">"Geüpdatet door uw beheerder"</string>
- <string name="package_deleted_device_owner" msgid="7650577387493101353">"Verwijderd door uw beheerder"</string>
- <string name="battery_saver_description" msgid="1960431123816253034">"Accubesparing beperkt de prestaties van uw apparaat, de trilstand, locatieservices en de meeste achtergrondgegevens om de gebruiksduur van de accu te verlengen.\n\nAccubesparing wordt automatisch uitgeschakeld terwijl uw apparaat wordt opgeladen."</string>
+ <string name="package_installed_device_owner" msgid="8420696545959087545">"Geïnstalleerd door je beheerder"</string>
+ <string name="package_updated_device_owner" msgid="8856631322440187071">"Geüpdatet door je beheerder"</string>
+ <string name="package_deleted_device_owner" msgid="7650577387493101353">"Verwijderd door je beheerder"</string>
+ <string name="battery_saver_description" msgid="1960431123816253034">"Accubesparing beperkt de prestaties van je apparaat, de trilstand, locatieservices en de meeste achtergrondgegevens om de gebruiksduur van de accu te verlengen.\n\nAccubesparing wordt automatisch uitgeschakeld terwijl je apparaat wordt opgeladen."</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d minuten (tot <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Eén minuut (tot <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
@@ -1480,8 +1480,8 @@
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evenement"</string>
<string name="muted_by" msgid="6147073845094180001">"Gedempt door <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
- <string name="system_error_wipe_data" msgid="6608165524785354962">"Er is een intern probleem met uw apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string>
- <string name="system_error_manufacturer" msgid="8086872414744210668">"Er is een intern probleem met uw apparaat. Neem contact op met de fabrikant voor meer informatie."</string>
+ <string name="system_error_wipe_data" msgid="6608165524785354962">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string>
+ <string name="system_error_manufacturer" msgid="8086872414744210668">"Er is een intern probleem met je apparaat. Neem contact op met de fabrikant voor meer informatie."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-verzoek is gewijzigd in DIAL-verzoek."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-verzoek is gewijzigd in SS-verzoek."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-verzoek is gewijzigd in nieuw USSD-verzoek."</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 82d1c02..0ef89a6 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -69,8 +69,8 @@
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
- <string name="ClipMmi" msgid="6952821216480289285">"Nazwa rozmówcy przy połączeniach przychodzących"</string>
- <string name="ClirMmi" msgid="7784673673446833091">"Nazwa rozmówcy przy połączeniach wychodzących"</string>
+ <string name="ClipMmi" msgid="6952821216480289285">"ID rozmówcy przy połączeniach przychodzących"</string>
+ <string name="ClirMmi" msgid="7784673673446833091">"ID rozmówcy przy połączeniach wychodzących"</string>
<string name="ColpMmi" msgid="3065121483740183974">"Identyfikator połączonej linii"</string>
<string name="ColrMmi" msgid="4996540314421889589">"Ograniczenie identyfikatora połączonej linii"</string>
<string name="CfMmi" msgid="5123218989141573515">"Przekierowanie połączeń"</string>
@@ -84,12 +84,12 @@
<string name="RuacMmi" msgid="7827887459138308886">"Odrzucanie niepożądanych, irytujących połączeń"</string>
<string name="CndMmi" msgid="3116446237081575808">"Dostarczanie numeru telefonującego"</string>
<string name="DndMmi" msgid="1265478932418334331">"Nie przeszkadzać"</string>
- <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Nazwa rozmówcy ustawiona jest domyślnie na „zastrzeżony”. Następne połączenie: zastrzeżony"</string>
- <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Nazwa rozmówcy ustawiona jest domyślnie na „zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
- <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Nazwa rozmówcy ustawiona jest domyślnie na „nie zastrzeżony”. Następne połączenie: zastrzeżony"</string>
- <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Nazwa rozmówcy ustawiona jest domyślnie na „nie zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
+ <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"ID rozmówcy ustawiony jest domyślnie na „zastrzeżony”. Następne połączenie: zastrzeżony"</string>
+ <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"ID rozmówcy ustawiony jest domyślnie na „zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
+ <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: zastrzeżony"</string>
+ <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"Usługa nie jest świadczona."</string>
- <string name="CLIRPermanent" msgid="3377371145926835671">"Nie możesz zmienić ustawienia identyfikatora rozmówcy."</string>
+ <string name="CLIRPermanent" msgid="3377371145926835671">"Nie możesz zmienić ustawienia ID rozmówcy."</string>
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Zmieniono ograniczenie dostępu"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Usługa transmisji danych jest zablokowana."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Usługa połączeń alarmowych jest zablokowana."</string>
diff --git a/core/res/res/values-ta-rIN-watch/strings.xml b/core/res/res/values-ta-rIN-watch/strings.xml
index 63e072f..629ca27 100644
--- a/core/res/res/values-ta-rIN-watch/strings.xml
+++ b/core/res/res/values-ta-rIN-watch/strings.xml
@@ -29,7 +29,7 @@
<string name="permgrouplab_storagewear" msgid="1003807594193602313">"உங்கள் வாட்சில் உள்ள படங்கள், மீடியா மற்றும் கோப்புகளை அணுகும்"</string>
<string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ஆடியோவைப் பதிவுசெய்யும்"</string>
<string name="permgrouplab_camerawear" msgid="4543951283103407017">"படங்களை எடுக்கும், வீடியோவைப் பதிவுசெய்யும்"</string>
- <string name="permgrouplab_phonewear" msgid="134365036753766126">"மொபைல் அழைப்புகளைச் செய்யும், பெறும்"</string>
+ <string name="permgrouplab_phonewear" msgid="134365036753766126">"மொபைல் அழைப்புகளைச் செய்யும், நிர்வகிக்கும்"</string>
<string name="permgrouplab_sensorswear" msgid="1429324744329327663">"உங்கள் உடலியக்கக் குறிகள் பற்றிய உணர்வித் தரவை அணுகும்"</string>
<string name="permlab_statusBarServicewear" msgid="2469402818964691034">"நிலைப் பட்டியில் இருக்கும்"</string>
<string name="permlab_bodySensorswear" msgid="7857941041202791873">"உடல் உணர்விகளை (இதயத் துடிப்பு மானிட்டர்கள் போன்றவை) அணுகும்"</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-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index aa46949..e6e20f1 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -429,7 +429,7 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"فنگر پرنٹ اسٹور نہیں کیا جا سکتا ہے۔ براہ کرم ایک موجودہ فنگر پرنٹ ہٹائیں۔"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"فنگر پرنٹ کی میعاد ختم ہوگئی۔ دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"فنگر پرنٹ کی کارروائی منسوخ ہوگئی۔"</string>
- <string name="fingerprint_error_lockout" msgid="5536934748136933450">"کافی زیادہ کوششیں کی گئیں۔ بعد میں دوباہ کوشش کریں۔"</string>
+ <string name="fingerprint_error_lockout" msgid="5536934748136933450">"کافی زیادہ کوششیں کی گئیں۔ بعد میں دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"انگلی <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 9044802..41b05ea 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -51,4 +51,7 @@
<!-- 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>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 61af6c5..902d40d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -639,6 +639,8 @@
<attr name="imageButtonStyle" format="reference" />
<!-- The style resource to use for an ImageButton that is an image well. -->
<attr name="imageWellStyle" format="reference" />
+ <!-- Default menu-style ListView style. -->
+ <attr name="listMenuViewStyle" format="reference" />
<!-- Default ListView style. -->
<attr name="listViewStyle" format="reference" />
<!-- ListView with white background. -->
@@ -1818,6 +1820,7 @@
<enum name="KEYCODE_MEDIA_SKIP_BACKWARD" value="273" />
<enum name="KEYCODE_MEDIA_STEP_FORWARD" value="274" />
<enum name="KEYCODE_MEDIA_STEP_BACKWARD" value="275" />
+ <enum name="KEYCODE_SOFT_SLEEP" value="276" />
</attr>
<!-- ***************************************************************** -->
@@ -3784,6 +3787,8 @@
<attr name="itemIconDisabledAlpha" format="float" />
<!-- Whether space should be reserved in layout when an icon is missing. -->
<attr name="preserveIconSpacing" format="boolean" />
+ <!-- Drawable for the arrow icon indicating a particular item is a submenu. -->
+ <attr name="subMenuArrow" format="reference" />
</declare-styleable>
<declare-styleable name="IconMenuView">
<!-- Defines the height of each row. -->
@@ -7764,10 +7769,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/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 4531f75..091d57f 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -24,7 +24,7 @@
<!-- The overall theme to use for an activity. Use with either the
application tag (to supply a default theme for all activities) or
the activity tag (to supply a specific theme for that activity).
-
+
<p>This automatically sets
your activity's Context to use this theme, and may also be used
for "starting" animations prior to the activity being launched (to
@@ -39,7 +39,7 @@
tag (to supply a specific label for that component). It may also be
used with the intent-filter tag to supply a label to show to the
user when an activity is being selected based on a particular Intent.
-
+
<p>The given label will be used wherever the user sees information
about its associated component; for example, as the name of a
main activity that is displayed in the launcher. You should
@@ -47,7 +47,7 @@
it can be localized, however it is also allowed to supply a plain
string for quick and dirty programming. -->
<attr name="label" format="reference|string" />
-
+
<!-- A Drawable resource providing a graphical representation of its
associated item. Use with the
application tag (to supply a default icon for all application
@@ -55,7 +55,7 @@
tag (to supply a specific icon for that component). It may also be
used with the intent-filter tag to supply an icon to show to the
user when an activity is being selected based on a particular Intent.
-
+
<p>The given icon will be used to display to the user a graphical
representation of its associated component; for example, as the icon
for main activity that is displayed in the launcher. This must be
@@ -95,15 +95,15 @@
<!-- Name of the activity to be launched to manage application's space on
device. The specified activity gets automatically launched when the
- application's space needs to be managed and is usually invoked
+ application's space needs to be managed and is usually invoked
through user actions. Applications can thus provide their own custom
behavior for managing space for various scenarios like out of memory
conditions. This is an optional attribute and
- applications can choose not to specify a default activity to
+ applications can choose not to specify a default activity to
manage space. -->
<attr name="manageSpaceActivity" format="string" />
- <!-- Option to let applications specify that user data can/cannot be
+ <!-- Option to let applications specify that user data can/cannot be
cleared. This flag is turned on by default.
<em>This attribute is usable only by applications
included in the system image. Third-party apps cannot use it.</em> -->
@@ -122,31 +122,31 @@
kind of application can not be installed without the
INSTALL_ALLOW_TEST flag, which means only through adb install. -->
<attr name="testOnly" format="boolean" />
-
+
<!-- A unique name for the given item. This must use a Java-style naming
convention to ensure the name is unique, for example
- "com.mycompany.MyName". -->
+ "com.mycompany.MyName". -->
<attr name="name" format="string" />
-
+
<!-- Specify a permission that a client is required to have in order to
use the associated object. If the client does not hold the named
permission, its request will fail. See the
<a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
document for more information on permissions. -->
<attr name="permission" format="string" />
-
+
<!-- A specific {@link android.R.attr#permission} name for read-only
access to a {@link android.content.ContentProvider}. See the
<a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
document for more information on permissions. -->
<attr name="readPermission" format="string" />
-
+
<!-- A specific {@link android.R.attr#permission} name for write
access to a {@link android.content.ContentProvider}. See the
<a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
document for more information on permissions. -->
<attr name="writePermission" format="string" />
-
+
<!-- If true, the {@link android.content.Context#grantUriPermission
Context.grantUriPermission} or corresponding Intent flags can
be used to allow others to access specific URIs in the content
@@ -156,7 +156,7 @@
Context.revokeUriPermission} when URIs are deleted from your
provider.-->
<attr name="grantUriPermissions" format="boolean" />
-
+
<!-- Characterizes the potential risk implied in a permission and
indicates the procedure the system should follow when determining
whether to grant the permission to an application requesting it. {@link
@@ -257,7 +257,7 @@
with. The group must have been defined with the
{@link android.R.styleable#AndroidManifestPermissionGroup permission-group} tag. -->
<attr name="permissionGroup" format="string" />
-
+
<!-- Specify the name of a user ID that will be shared between multiple
packages. By default, each package gets its own unique user-id.
By setting this value on two or more packages, each of these packages
@@ -265,13 +265,13 @@
in the same process. Note that for them to actually get the same
user ID, they must also be signed with the same signature. -->
<attr name="sharedUserId" format="string" />
-
+
<!-- Specify a label for the shared user UID of this package. This is
only used if you have also used android:sharedUserId. This must
be a reference to a string resource; it can not be an explicit
string. -->
<attr name="sharedUserLabel" format="reference" />
-
+
<!-- Internal version code. This is the number used to determine whether
one version is more recent than another: it has no other meaning than
that higher numbers are more recent. You could use this number to
@@ -279,7 +279,7 @@
number, simply increase it by one each time a new version is
released, or define it however else you want, as long as each
successive version has a higher number. This is not a version
- number generally shown to the user, that is usually supplied
+ number generally shown to the user, that is usually supplied
with {@link android.R.attr#versionName}. When an app is delivered
as multiple split APKs, each APK must have the exact same versionCode. -->
<attr name="versionCode" format="integer" />
@@ -296,7 +296,7 @@
is used for no other purpose than display to the user; the actual
significant version number is given by {@link android.R.attr#versionCode}. -->
<attr name="versionName" format="string" />
-
+
<!-- Flag to control special persistent mode of an application. This should
not normally be used by applications; it requires that the system keep
your application running at all times. -->
@@ -309,7 +309,7 @@
<!-- Flag indicating whether the application can be debugged, even when
running on a device that is running in user mode. -->
<attr name="debuggable" format="boolean" />
-
+
<!-- Flag indicating whether the application requests the VM to operate in
the safe mode. -->
<attr name="vmSafeMode" format="boolean" />
@@ -367,7 +367,7 @@
Use with the application tag (to supply a default process for all
application components), or with the activity, receiver, service,
or provider tag (to supply a specific icon for that component).
-
+
<p>Application components are normally run in a single process that
is created for the entire application. You can use this tag to modify
where they run. If the process name begins with a ':' character,
@@ -378,12 +378,12 @@
provided that you have permission to do so, allowing multiple
applications to share one process to reduce resource usage. -->
<attr name="process" format="string" />
-
+
<!-- Specify a task name that activities have an "affinity" to.
Use with the application tag (to supply a default affinity for all
activities in the application), or with the activity tag (to supply
a specific affinity for that component).
-
+
<p>The default value for this attribute is the same as the package
name, indicating that all activities in the manifest should generally
be considered a single "application" to the user. You can use this
@@ -392,13 +392,13 @@
task from the user's perspective, or using an empty string for
activities that have no affinity to a task. -->
<attr name="taskAffinity" format="string" />
-
+
<!-- Specify that an activity can be moved out of a task it is in to
the task it has an affinity for when appropriate. Use with the
application tag (to supply a default for all activities in the
application), or with an activity tag (to supply a specific
setting for that component).
-
+
<p>Normally when an application is started, it is associated with
the task of the activity that started it and stays there for its
entire lifetime. You can use the allowTaskReparenting feature to force an
@@ -422,17 +422,17 @@
applications' processes. On devices that support multiple instruction sets,
this implies the code might be loaded into a process that's using any of the devices
supported instruction sets.
-
+
<p> The system might treat such applications specially, for eg., by
extracting the application's native libraries for all supported instruction
sets or by compiling the application's dex code for all supported instruction
sets. -->
<attr name="multiArch" format ="boolean" />
-
+
<!-- Specify whether a component is allowed to have multiple instances
of itself running in different processes. Use with the activity
and provider tags.
-
+
<p>Normally the system will ensure that all instances of a particular
component are only running in a single process. You can use this
attribute to disable that behavior, allowing the system to create
@@ -441,28 +441,28 @@
of a provider can be created in each client process, allowing them
to be used without performing IPC. -->
<attr name="multiprocess" format="boolean" />
-
+
<!-- Specify whether an activity should be finished when its task is
brought to the foreground by relaunching from the home screen.
-
+
<p>If both this option and {@link android.R.attr#allowTaskReparenting} are
specified, the finish trumps the affinity: the affinity will be
ignored and the activity simply finished. -->
<attr name="finishOnTaskLaunch" format="boolean" />
-
+
<!-- Specify whether an activity should be finished when a "close system
windows" request has been made. This happens, for example, when
the home key is pressed, when the device is locked, when a system
dialog showing recent applications is displayed, etc. -->
<attr name="finishOnCloseSystemDialogs" format="boolean" />
-
+
<!-- Specify whether an activity's task should be cleared when it
is re-launched from the home screen. As a result, every time the
user starts the task, they will be brought to its root activity,
regardless of whether they used BACK or HOME to last leave it.
This flag only applies to activities that
are used to start the root of a new task.
-
+
<p>An example of the use of this flag would be for the case where
a user launches activity A from home, and from there goes to
activity B. They now press home, and then return to activity A.
@@ -471,7 +471,7 @@
then upon going to the background all of the tasks on top of it (B
in this case) are removed, so when the user next returns to A they
will restart at its original activity.
-
+
<p>When this option is used in conjunction with
{@link android.R.attr#allowTaskReparenting}, the allowTaskReparenting trumps the
clear. That is, all activities above the root activity of the
@@ -479,30 +479,30 @@
to the task they are associated with, otherwise they will simply
be dropped as described here. -->
<attr name="clearTaskOnLaunch" format="boolean" />
-
+
<!-- Specify whether an activity should be kept in its history stack.
If this attribute is set, then as soon as the user navigates away
from the activity it will be finished and they will no longer be
able to return to it. -->
<attr name="noHistory" format="boolean" />
-
+
<!-- Specify whether an acitivty's task state should always be maintained
by the system, or if it is allowed to reset the task to its initial
state in certain situations.
-
+
<p>Normally the system will reset a task (remove all activities from
the stack and reset the root activity) in certain situations when
the user re-selects that task from the home screen. Typically this
will be done if the user hasn't visited that task for a certain
amount of time, such as 30 minutes.
-
+
<p>By setting this attribute, the user will always return to your
task in its last state, regardless of how they get there. This is
useful, for example, in an application like the web browser where there
is a lot of state (such as multiple open tabs) that the application
would not like to lose. -->
<attr name="alwaysRetainTaskState" format="boolean" />
-
+
<!-- Indicates that an Activity does not need to have its freeze state
(as returned by {@link android.app.Activity#onSaveInstanceState}
retained in order to be restarted. Generally you use this for activities
@@ -512,7 +512,7 @@
it normally would. Instead, the next time the user navigates to
it its {@link android.app.Activity#onCreate} method will be called
with a null icicle, just like it was starting for the first time.
-
+
<p>This is used by the Home activity to make sure it does not get
removed if it crashes for some reason. -->
<attr name="stateNotNeeded" format="boolean" />
@@ -539,11 +539,11 @@
in order to avoid conflicts. Typically this name is the same
as the class implementation describing the provider's data structure. -->
<attr name="authorities" format="string" />
-
+
<!-- Flag indicating whether this content provider would like to
participate in data synchronization. -->
<attr name="syncable" format="boolean" />
-
+
<!-- Flag declaring this activity to be 'immersive'; immersive activities
should not be interrupted with other activities or notifications. -->
<attr name="immersive" format="boolean" />
@@ -555,7 +555,7 @@
The value is a simple integer, with higher numbers being
initialized first. -->
<attr name="initOrder" format="integer" />
-
+
<!-- Specify the relative importance or ability in handling a particular
Intent. For receivers, this controls the order in which they are
executed to receive a broadcast (note that for
@@ -564,18 +564,18 @@
Intent; when multiple activities match an intent and have different
priorities, only those with the higher priority value will be
considered a match.
-
+
<p>Only use if you really need to impose some specific
order in which the broadcasts are received, or want to forcibly
place an activity to always be preferred over others. The value is a
single integer, with higher numbers considered to be better. -->
<attr name="priority" format="integer" />
-
+
<!-- Specify how an activity should be launched. See the
<a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Stack</a> document for important information on how these options impact
the behavior of your application.
-
+
<p>If this attribute is not specified, <code>standard</code> launch
mode will be used. Note that the particular launch behavior can
be changed in some ways at runtime through the
@@ -612,19 +612,19 @@
<a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Stack</a> document for more details about tasks.-->
<enum name="singleTask" value="2" />
- <!-- Only allow one instance of this activity to ever be
- running. This activity gets a unique task with only itself running
- in it; if it is ever launched again with the same Intent, then that
- task will be brought forward and its
+ <!-- Only allow one instance of this activity to ever be
+ running. This activity gets a unique task with only itself running
+ in it; if it is ever launched again with the same Intent, then that
+ task will be brought forward and its
{@link android.app.Activity#onNewIntent Activity.onNewIntent()}
- method called. If this
- activity tries to start a new activity, that new activity will be
+ method called. If this
+ activity tries to start a new activity, that new activity will be
launched in a separate task. See the
<a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Stack</a> document for more details about tasks.-->
<enum name="singleInstance" value="3" />
</attr>
-
+
<!-- Specify the orientation an activity should be run in. If not
specified, it will run in the current preferred orientation
of the screen.
@@ -638,7 +638,7 @@
if this activity is the bottom of a task. If the user
explicitly turned off sensor based orientation through settings
sensor based device rotation will be ignored. If not by default
- sensor based orientation will be taken into account and the
+ sensor based orientation will be taken into account and the
orientation will changed based on how the user rotates the device.
Corresponds to
{@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED}. -->
@@ -726,19 +726,19 @@
{@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LOCKED}. -->
<enum name="locked" value="14" />
</attr>
-
+
<!-- Specify one or more configuration changes that the activity will
handle itself. If not specified, the activity will be restarted
if any of these configuration changes happen in the system. Otherwise,
the activity will remain running and its
{@link android.app.Activity#onConfigurationChanged Activity.onConfigurationChanged}
method called with the new configuration.
-
+
<p>Note that all of these configuration changes can impact the
resource values seen by the application, so you will generally need
to re-retrieve all resources (including view layouts, drawables, etc)
to correctly handle any configuration change.
-
+
<p>These values must be kept in sync with those in
{@link android.content.pm.ActivityInfo} and
include/utils/ResourceTypes.h. -->
@@ -804,18 +804,18 @@
<!-- Descriptive text for the associated data. -->
<attr name="description" format="reference" />
-
+
<!-- The name of the application package that an Instrumentation object
will run against. -->
<attr name="targetPackage" format="string" />
-
+
<!-- Flag indicating that an Instrumentation class wants to take care
of starting/stopping profiling itself, rather than relying on
the default behavior of profiling the complete time it is running.
This allows it to target profiling data at a specific set of
operations. -->
<attr name="handleProfiling" format="boolean" />
-
+
<!-- Flag indicating that an Instrumentation class should be run as a
functional test. -->
<attr name="functionalTest" format="boolean" />
@@ -1069,8 +1069,7 @@
<p>NOTE: The value of {@link android.R.attr#screenOrientation} will be ignored for
resizeable activities as the system doesn't support fixed orientation on a resizeable
- activity.
- @hide -->
+ activity. -->
<attr name="resizeableActivity" format="boolean" />
<!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
@@ -1271,7 +1270,7 @@
features in your package (or other packages). See the
<a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
document for more information on permissions.
-
+
<p>This appears as a child tag of the root
{@link #AndroidManifest manifest} tag. -->
<declare-styleable name="AndroidManifestPermission" parent="AndroidManifest">
@@ -1290,15 +1289,15 @@
<attr name="protectionLevel" />
<attr name="permissionFlags" />
</declare-styleable>
-
+
<!-- The <code>permission-group</code> tag declares a logical grouping of
related permissions.
-
+
<p>Note that this tag does not declare a permission itself, only
a namespace in which further permissions can be placed. See
the {@link #AndroidManifestPermission <permission>} tag for
more information.
-
+
<p>This appears as a child tag of the root
{@link #AndroidManifest manifest} tag. -->
<declare-styleable name="AndroidManifestPermissionGroup" parent="AndroidManifest">
@@ -1316,7 +1315,7 @@
<attr name="permissionGroupFlags" />
<attr name="priority" />
</declare-styleable>
-
+
<!-- The <code>permission-tree</code> tag declares the base of a tree of
permission values: it declares that this package has ownership of
the given permission name, as well as all names underneath it
@@ -1324,12 +1323,12 @@
{@link android.content.pm.PackageManager#addPermission
PackageManager.addPermission()} method to dynamically add new
permissions under this tree.
-
+
<p>Note that this tag does not declare a permission itself, only
a namespace in which further permissions can be placed. See
the {@link #AndroidManifestPermission <permission>} tag for
more information.
-
+
<p>This appears as a child tag of the root
{@link #AndroidManifest manifest} tag. -->
<declare-styleable name="AndroidManifestPermissionTree" parent="AndroidManifest">
@@ -1346,7 +1345,7 @@
<attr name="banner" />
<attr name="logo" />
</declare-styleable>
-
+
<!-- The <code>uses-permission</code> tag requests a
{@link #AndroidManifestPermission <permission>} that the containing
package must be granted in order for it to operate correctly. For runtime
@@ -1359,7 +1358,7 @@
document for more information on permissions. Also available is a
{@link android.Manifest.permission list of permissions} included
with the base platform.
-
+
<p>This appears as a child tag of the root
{@link #AndroidManifest manifest} tag. -->
<declare-styleable name="AndroidManifestUsesPermission" parent="AndroidManifest">
@@ -1436,7 +1435,7 @@
<!-- The <code>uses-sdk</code> tag describes the SDK features that the
containing package must be running on to operate correctly.
-
+
<p>This appears as a child tag of the root
{@link #AndroidManifest manifest} tag. -->
<declare-styleable name="AndroidManifestUsesSdk" parent="AndroidManifest">
@@ -1464,7 +1463,7 @@
incompatibility with them. -->
<attr name="maxSdkVersion" />
</declare-styleable>
-
+
<!-- The <code>library</code> tag declares that this apk is providing itself
as a shared library for other applications to use. It can only be used
with apks that are built in to the system image. Other apks can link to
@@ -1483,7 +1482,7 @@
<!-- The <code>uses-libraries</code> specifies a shared library that this
package requires to be linked against. Specifying this flag tells the
system to include this library's code in your class loader.
-
+
<p>This appears as a child tag of the
{@link #AndroidManifestApplication application} tag. -->
<declare-styleable name="AndroidManifestUsesLibrary" parent="AndroidManifestApplication">
@@ -1498,7 +1497,7 @@
dynamically at runtime. -->
<attr name="required" />
</declare-styleable>
-
+
<!-- The <code>supports-screens</code> specifies the screen dimensions an
application supports. By default a modern application supports all
screen sizes and must explicitly disable certain screen sizes here;
@@ -1506,7 +1505,7 @@
(HVGA) screen size. Note that screen size is a separate axis from
density, and is determined as the available pixels to an application
after density scaling has been applied.
-
+
<p>This appears as a child tag of the
{@link #AndroidManifest manifest} tag. -->
<declare-styleable name="AndroidManifestSupportsScreens" parent="AndroidManifest">
@@ -1609,7 +1608,7 @@
{@link android.content.ContentProvider} class that is available
as part of the package's application components, supplying structured
access to data managed by the application.
-
+
<p>This appears as a child tag of the
{@link #AndroidManifestApplication application} tag. -->
<declare-styleable name="AndroidManifestProvider" parent="AndroidManifestApplication">
@@ -1641,7 +1640,7 @@
<attr name="exported" />
<attr name="singleUser" />
</declare-styleable>
-
+
<!-- Attributes that can be supplied in an AndroidManifest.xml
<code>grant-uri-permission</code> tag, a child of the
{@link #AndroidManifestProvider provider} tag, describing a specific
@@ -1658,7 +1657,7 @@
<attr name="pathPrefix" format="string" />
<!-- Specify a URI path that matches a simple pattern, as per
{@link android.os.PatternMatcher} with
- {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
+ {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
Note that because '\' is used as an escape character when
reading the string from XML (before it is parsed as a pattern),
you will need to double-escape: for example a literal "*" would
@@ -1667,7 +1666,7 @@
write if constructing the string in Java code. -->
<attr name="pathPattern" format="string" />
</declare-styleable>
-
+
<!-- Attributes that can be supplied in an AndroidManifest.xml
<code>path-permission</code> tag, a child of the
{@link #AndroidManifestProvider provider} tag, describing a permission
@@ -1681,13 +1680,13 @@
<attr name="readPermission" />
<attr name="writePermission" />
</declare-styleable>
-
+
<!-- The <code>service</code> tag declares a
{@link android.app.Service} class that is available
as part of the package's application components, implementing
long-running background operations or a rich communication API
that can be called by other packages.
-
+
<p>Zero or more {@link #AndroidManifestIntentFilter intent-filter}
tags can be included inside of a service, to specify the Intents
that can connect with it. If none are specified, the service can
@@ -1724,13 +1723,13 @@
<attr name="isolatedProcess" format="boolean" />
<attr name="singleUser" />
</declare-styleable>
-
+
<!-- The <code>receiver</code> tag declares an
{@link android.content.BroadcastReceiver} class that is available
as part of the package's application components, allowing the
application to receive actions or data broadcast by other
applications even if it is not currently running.
-
+
<p>Zero or more {@link #AndroidManifestIntentFilter intent-filter}
tags can be included inside of a receiver, to specify the Intents
it will receive. If none are specified, the receiver will only
@@ -1826,7 +1825,6 @@
<attr name="autoRemoveFromRecents" />
<attr name="relinquishTaskIdentity" />
<attr name="resumeWhilePausing" />
- <!-- @hide -->
<attr name="resizeableActivity" />
<attr name="lockTaskMode" />
<attr name="showForAllUsers" />
@@ -1878,7 +1876,7 @@
data by the system. You may supply the data through either the
<code>value</code> or <code>resource</code> attribute; if both
are given, then <code>resource</code> will be used.
-
+
<p>It is highly recommended that you avoid supplying related data as
multiple separate meta-data entries. Instead, if you have complex
data to associate with a component, then use the <code>resource</code>
@@ -1907,22 +1905,22 @@
Bundle through {@link android.os.Bundle#getInt Bundle.getInt}. -->
<attr name="resource" format="reference" />
</declare-styleable>
-
+
<!-- The <code>intent-filter</code> tag is used to construct an
{@link android.content.IntentFilter} object that will be used
to determine which component can handle a particular
{@link android.content.Intent} that has been given to the system.
It can be used as a child of the
{@link #AndroidManifestActivity activity},
- {@link #AndroidManifestReceiver receiver} and
+ {@link #AndroidManifestReceiver receiver} and
{@link #AndroidManifestService service}
tags.
-
+
<p> Zero or more {@link #AndroidManifestAction action},
{@link #AndroidManifestCategory category}, and/or
{@link #AndroidManifestData data} tags should be
included inside to describe the contents of the filter.
-
+
<p> The optional label and icon attributes here are used with
an activity to supply an alternative description of that activity
when it is being started through an Intent matching this filter. -->
@@ -1935,7 +1933,7 @@
<attr name="priority" />
<attr name="autoVerify" />
</declare-styleable>
-
+
<!-- Attributes that can be supplied in an AndroidManifest.xml
<code>action</code> tag, a child of the
{@link #AndroidManifestIntentFilter intent-filter} tag.
@@ -1950,7 +1948,7 @@
package name. -->
<attr name="name" />
</declare-styleable>
-
+
<!-- Attributes that can be supplied in an AndroidManifest.xml
<code>data</code> tag, a child of the
{@link #AndroidManifestIntentFilter intent-filter} tag, describing
@@ -2022,7 +2020,7 @@
<!-- Specify a URI path that matches a simple pattern, as per
{@link android.content.IntentFilter#addDataPath
IntentFilter.addDataPath()} with
- {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
+ {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
Note that because '\' is used as an escape character when
reading the string from XML (before it is parsed as a pattern),
you will need to double-escape: for example a literal "*" would
@@ -2031,10 +2029,10 @@
write if constructing the string in Java code. -->
<attr name="pathPattern" />
</declare-styleable>
-
+
<!-- Attributes that can be supplied in an AndroidManifest.xml
<code>category</code> tag, a child of the
- {@link #AndroidManifestIntentFilter intent-filter} tag.
+ {@link #AndroidManifestIntentFilter intent-filter} tag.
See {@link android.content.IntentFilter#addCategory} for
more information. -->
<declare-styleable name="AndroidManifestCategory" parent="AndroidManifestIntentFilter">
@@ -2046,7 +2044,7 @@
package name. -->
<attr name="name" />
</declare-styleable>
-
+
<!-- Attributes that can be supplied in an AndroidManifest.xml
<code>instrumentation</code> tag, a child of the root
{@link #AndroidManifest manifest} tag. -->
@@ -2065,7 +2063,7 @@
<attr name="handleProfiling" />
<attr name="functionalTest" />
</declare-styleable>
-
+
<!-- Attributes that can be supplied in an AndroidManifest.xml
<code>screen</code> tag, a child of <code>compatible-screens</code>,
which is itself a child of the root
@@ -2162,14 +2160,14 @@
{@link android.content.Intent#setComponent Intent.setComponent()}. -->
<attr name="targetClass" format="string" />
</declare-styleable>
-
+
<!-- A category to add to an Intent, as per
{@link android.content.Intent#addCategory Intent.addCategory()}. -->
<declare-styleable name="IntentCategory" parent="Intent">
<!-- Required name of the category. -->
<attr name="name" />
</declare-styleable>
-
+
<!-- An extra data value to place into a an extra/name value pair held
in a Bundle, as per {@link android.os.Bundle}. -->
<declare-styleable name="Extra" parent="Intent">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
old mode 100755
new mode 100644
index ef4e261..a5960a9
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -170,6 +170,21 @@
so that applications can still use their own mechanisms. -->
<bool name="config_enableAutoPowerModes">false</bool>
+ <!-- The threshold angle for any motion detection in auto-power save modes.
+ In hundreths of a degree. -->
+ <integer name="config_autoPowerModeThresholdAngle">200</integer>
+
+ <!-- The sensor id of an "any motion" sensor used in auto-power save modes.
+ 0 indicates this sensor is not available. -->
+ <integer name="config_autoPowerModeAnyMotionSensor">0</integer>
+
+ <!-- If an any motion sensor is not available, prefer the wrist tilt detector over the
+ SMD. -->
+ <bool name="config_autoPowerModePreferWristTilt">false</bool>
+
+ <!-- If a location should be pre-fetched when going into device idle. -->
+ <bool name="config_autoPowerModePrefetchLocation">true</bool>
+
<!-- The duration (in milliseconds) that the radio will scan for a signal
when there's no network connection. If the scan doesn't timeout, use zero -->
<integer name="config_radioScanningTimeout">0</integer>
@@ -237,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>
@@ -391,6 +406,10 @@
<!-- Boolean indicating autojoin will prefer 5GHz and choose 5GHz BSSIDs -->
<bool translatable="true" name="config_wifi_enable_5GHz_preference">true</bool>
+ <!-- Boolean indicating whether or not to revert to default country code when cellular
+ 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>
+
<!-- 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>
@@ -524,6 +543,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>
@@ -2278,9 +2300,24 @@
<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
must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
<string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string>
+
+ <!-- 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..8635a4f 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 -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d2089cd..b6284c9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2660,6 +2660,20 @@
=============================================================== -->
<eat-comment />
+ <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="style" name="Theme.Material.DayNight" />
<public type="style" name="Theme.Material.DayNight.DarkActionBar" />
<public type="style" name="Theme.Material.DayNight.Dialog" />
@@ -2679,7 +2693,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" />
</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 2369c9b..5dc14e3 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -685,6 +685,10 @@
<style name="Widget.Material.ListView.White"/>
+ <style name="Widget.Material.ListMenuView">
+ <item name="subMenuArrow">@drawable/ic_arrow_drop_right_black_24dp</item>
+ </style>
+
<style name="Widget.Material.PopupWindow" parent="Widget.PopupWindow"/>
<style name="Widget.Material.PopupWindow.ActionMode">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
old mode 100755
new mode 100644
index e04c743..16e44b8
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -158,6 +158,7 @@
<java-symbol type="id" name="shortcut" />
<java-symbol type="id" name="skip_button" />
<java-symbol type="id" name="split_action_bar" />
+ <java-symbol type="id" name="submenuarrow" />
<java-symbol type="id" name="submit_area" />
<java-symbol type="id" name="switch_new" />
<java-symbol type="id" name="switch_old" />
@@ -253,6 +254,10 @@
<java-symbol type="bool" name="config_cellBroadcastAppLinks" />
<java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
<java-symbol type="bool" name="config_enableAutoPowerModes" />
+ <java-symbol type="integer" name="config_autoPowerModeThresholdAngle" />
+ <java-symbol type="integer" name="config_autoPowerModeAnyMotionSensor" />
+ <java-symbol type="bool" name="config_autoPowerModePreferWristTilt" />
+ <java-symbol type="bool" name="config_autoPowerModePrefetchLocation" />
<java-symbol type="bool" name="config_enable_emergency_call_while_sim_locked" />
<java-symbol type="bool" name="config_enable_puk_unlock_screen" />
<java-symbol type="bool" name="config_enableBurnInProtection" />
@@ -301,6 +306,7 @@
<java-symbol type="bool" name="config_wifi_only_link_same_credential_configurations" />
<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_supportMicNearUltrasound" />
<java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
@@ -402,6 +408,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" />
@@ -1626,6 +1633,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" />
@@ -2185,6 +2193,8 @@
<java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
<java-symbol type="id" name="profile_button" />
+ <java-symbol type="bool" name="config_enableCascadingSubmenus" />
+
<!-- From SignalStrength -->
<java-symbol type="integer" name="config_LTE_RSRP_threshold_type" />
@@ -2307,6 +2317,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 e406ae0..3010190 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -238,6 +238,7 @@
<item name="gridViewStyle">@style/Widget.Material.GridView</item>
<item name="imageButtonStyle">@style/Widget.Material.ImageButton</item>
<item name="imageWellStyle">@style/Widget.Material.ImageWell</item>
+ <item name="listMenuViewStyle">@style/Widget.Material.ListMenuView</item>
<item name="listViewStyle">@style/Widget.Material.ListView</item>
<item name="listViewWhiteStyle">@style/Widget.Material.ListView.White</item>
<item name="popupWindowStyle">@style/Widget.Material.PopupWindow</item>
@@ -594,6 +595,7 @@
<item name="gridViewStyle">@style/Widget.Material.Light.GridView</item>
<item name="imageButtonStyle">@style/Widget.Material.Light.ImageButton</item>
<item name="imageWellStyle">@style/Widget.Material.Light.ImageWell</item>
+ <item name="listMenuViewStyle">@style/Widget.Material.ListMenuView</item>
<item name="listViewStyle">@style/Widget.Material.Light.ListView</item>
<item name="listViewWhiteStyle">@style/Widget.Material.Light.ListView.White</item>
<item name="popupWindowStyle">@style/Widget.Material.Light.PopupWindow</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/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
index e43ba12..7f9d874 100644
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
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/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
index c66fd15..922bc59 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
@@ -11,6 +11,7 @@
public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+ private static final long POLL_INTERVAL = 100; // ms
private AnimatorSetActivity mActivity;
private ObjectAnimator a1,a2,a3;
private ValueAnimator a4,a5;
@@ -481,6 +482,154 @@
});
}
+ @SmallTest
+ public void testClone() throws Throwable {
+ // Set up an AnimatorSet and two clones, add one listener to each. When the clones animate,
+ // listeners of both the clone and the animator being cloned should receive animation
+ // lifecycle events.
+ final AnimatorSet s1 = getSequentialSet();
+
+ // Record animators that called their listeners for the corresponding event.
+ final ArrayList<Animator> startedAnimators = new ArrayList<>();
+ final ArrayList<Animator> canceledAnimators = new ArrayList<>();
+ final ArrayList<Animator> endedAnimators = new ArrayList<>();
+
+ final MyListener l1 = new MyListener() {
+ @Override
+ public void onAnimationStart(Animator anim) {
+ super.onAnimationStart(anim);
+ startedAnimators.add(anim);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator anim) {
+ super.onAnimationCancel(anim);
+ canceledAnimators.add(anim);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator anim) {
+ super.onAnimationEnd(anim);
+ endedAnimators.add(anim);
+ }
+
+ };
+ s1.addListener(l1);
+
+ // Start the animation, and make the first clone during its run and the second clone once
+ // it ends.
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(l1.startIsCalled);
+ assertFalse(l1.endIsCalled);
+
+ s1.start();
+ }
+ });
+
+ // Make the first clone, during the animation's run.
+ assertTrue(s1.isStarted());
+ final AnimatorSet s2 = s1.clone();
+ final MyListener l2 = new MyListener();
+ s2.addListener(l2);
+
+ Thread.sleep(POLL_INTERVAL);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ s1.end();
+ }
+ });
+
+ Thread.sleep(POLL_INTERVAL);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(l1.startIsCalled);
+ assertTrue(l1.endIsCalled);
+ }
+ });
+ Thread.sleep(POLL_INTERVAL);
+
+ // Make the second clone now.
+ final AnimatorSet s3 = s1.clone();
+ final MyListener l3 = new MyListener();
+ s3.addListener(l3);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Checking the fields before animations start.
+ assertFalse(l2.startIsCalled);
+ assertFalse(l2.cancelIsCalled);
+ assertFalse(l2.endIsCalled);
+ assertFalse(l3.startIsCalled);
+ assertFalse(l3.cancelIsCalled);
+ assertFalse(l3.endIsCalled);
+
+ s2.start();
+ s3.start();
+ }
+ });
+
+ Thread.sleep(POLL_INTERVAL);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Make sure the listeners receive the callbacks
+ // At this time only onAnimationStart() should be called.
+ assertTrue(l2.startIsCalled);
+ assertTrue(l3.startIsCalled);
+ assertFalse(l2.endIsCalled);
+ assertFalse(l3.endIsCalled);
+ assertFalse(l2.cancelIsCalled);
+ assertFalse(l3.cancelIsCalled);
+
+ s2.end();
+ s3.cancel();
+ }
+ });
+ Thread.sleep(POLL_INTERVAL);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Check that the new listeners for the new animations gets called for the events.
+ assertTrue(l2.startIsCalled);
+ assertFalse(l2.cancelIsCalled);
+ assertTrue(l2.endIsCalled);
+ assertTrue(l3.startIsCalled);
+ assertTrue(l3.cancelIsCalled);
+ assertTrue(l3.endIsCalled);
+
+ // Check that the listener on the animation that was being clone receive the
+ // animation lifecycle events for the clones.
+ assertTrue(onlyContains(startedAnimators, s1, s2, s3));
+ assertTrue(onlyContains(canceledAnimators, s3));
+ assertTrue(onlyContains(endedAnimators, s1, s2, s3));
+ }
+ });
+
+ }
+
+ /**
+ * Check that the animator list contains exactly the given animators and nothing else.
+ */
+ private boolean onlyContains(ArrayList<Animator> animators, AnimatorSet... sets) {
+ if (sets.length != animators.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < sets.length; i++) {
+ AnimatorSet set = sets[i];
+ if (!animators.contains(set)) {
+ return false;
+ }
+ }
+ return true;
+
+ }
+
// Create an AnimatorSet with all the animators running sequentially
private AnimatorSet getSequentialSet() {
AnimatorSet set = new AnimatorSet();
diff --git a/core/tests/coretests/src/android/animation/AutoCancelTest.java b/core/tests/coretests/src/android/animation/AutoCancelTest.java
index b1f88db..5810818 100644
--- a/core/tests/coretests/src/android/animation/AutoCancelTest.java
+++ b/core/tests/coretests/src/android/animation/AutoCancelTest.java
@@ -18,10 +18,12 @@
import android.os.Handler;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
+@Suppress // Failing
public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
boolean mAnimX1Canceled = 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/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index 2615a28..e6d3158 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -28,7 +28,6 @@
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
@@ -37,7 +36,6 @@
import java.util.List;
/** Unit test for SettingsProvider. */
-@Suppress // Failing.
public class SettingsProviderTest extends AndroidTestCase {
@MediumTest
public void testNameValueCache() {
@@ -53,84 +51,108 @@
assertEquals(1, r.delete(Settings.Secure.getUriFor("test_service"), null, null));
assertEquals(null, Settings.Secure.getString(r, "test_service"));
- // Try all the same things in the System table
- Settings.System.putString(r, "test_setting", "Value");
- assertEquals("Value", Settings.System.getString(r, "test_setting"));
-
- Settings.System.putString(r, "test_setting", "New");
- assertEquals("New", Settings.System.getString(r, "test_setting"));
-
- assertEquals(1, r.delete(Settings.System.getUriFor("test_setting"), null, null));
- assertEquals(null, Settings.System.getString(r, "test_setting"));
+ // Apps should not be able to use System settings.
+ try {
+ Settings.System.putString(r, "test_setting", "Value");
+ fail("IllegalArgumentException expected");
+ } catch (java.lang.IllegalArgumentException e) {
+ // expected
+ }
}
@MediumTest
- public void testRowNameContentUri() {
+ public void testRowNameContentUriForSecure() {
+ final String testKey = "testRowNameContentUriForSecure";
+ final String testValue = "testValue";
+ final String secondTestValue = "testValueNew";
+
+ try {
+ testRowNameContentUri(Settings.Secure.CONTENT_URI, Settings.Secure.NAME,
+ Settings.Secure.VALUE, testKey, testValue, secondTestValue);
+ } finally {
+ // clean up
+ Settings.Secure.putString(getContext().getContentResolver(), testKey, null);
+ }
+ }
+
+ @MediumTest
+ public void testRowNameContentUriForSystem() {
+ final String testKey = Settings.System.VIBRATE_ON;
+ assertTrue("Settings.System.PUBLIC_SETTINGS cannot be empty. We need to use one of it"
+ + " for testing. Only settings key in this collection will be accepted by the"
+ + " framework.", Settings.System.PUBLIC_SETTINGS.contains(testKey));
+ final String testValue = "0";
+ final String secondTestValue = "1";
+ final String oldValue =
+ Settings.System.getString(getContext().getContentResolver(), testKey);
+
+ try {
+ testRowNameContentUri(Settings.System.CONTENT_URI, Settings.System.NAME,
+ Settings.System.VALUE, testKey, testValue, secondTestValue);
+ } finally {
+ // restore old value
+ if (oldValue != null) {
+ Settings.System.putString(getContext().getContentResolver(), testKey, oldValue);
+ }
+ }
+ }
+
+ private void testRowNameContentUri(Uri table, String nameField, String valueField,
+ String testKey, String testValue, String secondTestValue) {
ContentResolver r = getContext().getContentResolver();
- assertEquals("content://settings/system/test_setting",
- Settings.System.getUriFor("test_setting").toString());
- assertEquals("content://settings/secure/test_service",
- Settings.Secure.getUriFor("test_service").toString());
+ ContentValues v = new ContentValues();
+ v.put(nameField, testKey);
+ v.put(valueField, testValue);
- // These tables use the row name (not ID) as their content URI.
- Uri tables[] = { Settings.System.CONTENT_URI, Settings.Secure.CONTENT_URI };
- for (Uri table : tables) {
- ContentValues v = new ContentValues();
- v.put(Settings.System.NAME, "test_key");
- v.put(Settings.System.VALUE, "Test");
- Uri uri = r.insert(table, v);
- assertEquals(table.toString() + "/test_key", uri.toString());
+ r.insert(table, v);
+ Uri uri = Uri.parse(table.toString() + "/" + testKey);
- // Query with a specific URI and no WHERE clause succeeds.
- Cursor c = r.query(uri, null, null, null, null);
- try {
- assertTrue(c.moveToNext());
- assertEquals("test_key", c.getString(c.getColumnIndex(Settings.System.NAME)));
- assertEquals("Test", c.getString(c.getColumnIndex(Settings.System.VALUE)));
- assertFalse(c.moveToNext());
- } finally {
- c.close();
- }
-
- // Query with a specific URI and a WHERE clause fails.
- try {
- r.query(uri, null, "1", null, null);
- fail("UnsupportedOperationException expected");
- } catch (UnsupportedOperationException e) {
- if (!e.toString().contains("WHERE clause")) throw e;
- }
-
- // Query with a tablewide URI and a WHERE clause succeeds.
- c = r.query(table, null, "name='test_key'", null, null);
- try {
- assertTrue(c.moveToNext());
- assertEquals("test_key", c.getString(c.getColumnIndex(Settings.System.NAME)));
- assertEquals("Test", c.getString(c.getColumnIndex(Settings.System.VALUE)));
- assertFalse(c.moveToNext());
- } finally {
- c.close();
- }
-
- v = new ContentValues();
- v.put(Settings.System.VALUE, "Toast");
- assertEquals(1, r.update(uri, v, null, null));
-
- c = r.query(uri, null, null, null, null);
- try {
- assertTrue(c.moveToNext());
- assertEquals("test_key", c.getString(c.getColumnIndex(Settings.System.NAME)));
- assertEquals("Toast", c.getString(c.getColumnIndex(Settings.System.VALUE)));
- assertFalse(c.moveToNext());
- } finally {
- c.close();
- }
-
- assertEquals(1, r.delete(uri, null, null));
+ // Query with a specific URI and no WHERE clause succeeds.
+ Cursor c = r.query(uri, null, null, null, null);
+ try {
+ assertTrue(c.moveToNext());
+ assertEquals(testKey, c.getString(c.getColumnIndex(nameField)));
+ assertEquals(testValue, c.getString(c.getColumnIndex(valueField)));
+ assertFalse(c.moveToNext());
+ } finally {
+ c.close();
}
- assertEquals(null, Settings.System.getString(r, "test_key"));
- assertEquals(null, Settings.Secure.getString(r, "test_key"));
+ // Query with a specific URI and a WHERE clause fails.
+ try {
+ r.query(uri, null, "1", null, null);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // Query with a tablewide URI and a WHERE clause succeeds.
+ c = r.query(table, null, "name='" + testKey + "'", null, null);
+ try {
+ assertTrue(c.moveToNext());
+ assertEquals(testKey, c.getString(c.getColumnIndex(nameField)));
+ assertEquals(testValue, c.getString(c.getColumnIndex(valueField)));
+ assertFalse(c.moveToNext());
+ } finally {
+ c.close();
+ }
+
+ v = new ContentValues();
+ // NAME is still needed, although the uri should be specific enough. Why?
+ v.put(nameField, testKey);
+ v.put(valueField, secondTestValue);
+ assertEquals(1, r.update(uri, v, null, null));
+
+ c = r.query(uri, null, null, null, null);
+ try {
+ assertTrue(c.moveToNext());
+ assertEquals(testKey, c.getString(c.getColumnIndex(nameField)));
+ assertEquals(secondTestValue, c.getString(c.getColumnIndex(valueField)));
+ assertFalse(c.moveToNext());
+ } finally {
+ c.close();
+ }
}
@MediumTest
@@ -139,7 +161,7 @@
ContentResolver r = getContext().getContentResolver();
// Make sure there's an owner
- assertTrue(findUser(um, UserHandle.USER_OWNER));
+ assertTrue(findUser(um, UserHandle.USER_SYSTEM));
// create a new user to use for testing
UserInfo otherUser = um.createUser("TestUser1", UserInfo.FLAG_GUEST);
@@ -148,21 +170,17 @@
assertNotSame("Current calling user id should not be the new guest user",
otherUser.id, UserHandle.getCallingUserId());
- Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "gps");
- Settings.Secure.putStringForUser(r,
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "network", otherUser.id);
+ final String testKey = "testSettingsChangeForOtherUser";
+ final String testValue1 = "value1";
+ final String testValue2 = "value2";
+ Settings.Secure.putString(r, testKey, testValue1);
+ Settings.Secure.putStringForUser(r, testKey, testValue2, otherUser.id);
- assertEquals("gps",
- Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
- assertEquals("network", Settings.Secure.getStringForUser(
- r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, otherUser.id));
+ assertEquals(testValue1, Settings.Secure.getString(r, testKey));
+ assertEquals(testValue2, Settings.Secure.getStringForUser(r, testKey, otherUser.id));
assertNotSame("Current calling user id should not be the new guest user",
otherUser.id, UserHandle.getCallingUserId());
- Settings.Secure.setLocationProviderEnabledForUser(r, "network", false, otherUser.id);
- assertEquals("", Settings.Secure.getStringForUser(
- r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, otherUser.id));
-
} finally {
// Tidy up
um.removeUser(otherUser.id);
@@ -170,6 +188,7 @@
}
@MediumTest
+ @Suppress // Settings.Bookmarks uses a query format that's not supported now.
public void testRowNumberContentUri() {
ContentResolver r = getContext().getContentResolver();
@@ -196,47 +215,56 @@
public void testParseProviderList() {
ContentResolver r = getContext().getContentResolver();
- // Make sure we get out what we put in.
- Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "test1,test2,test3");
- assertEquals(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED),
- "test1,test2,test3");
-
+ // We only accept "+value" and "-value"
// Test adding a value
- Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "");
Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+test1");
- assertEquals("test1",
- Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test1"));
// Test adding a second value
Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+test2");
- assertEquals("test1,test2",
- Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test1"));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test2"));
// Test adding a third value
Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+test3");
- assertEquals("test1,test2,test3",
- Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test1"));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test2"));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test3"));
// Test deleting the first value in a 3 item list
Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test1");
- assertEquals("test2,test3",
- Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+ assertFalse(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test1"));
// Test deleting the middle value in a 3 item list
- Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "test1,test2,test3");
- Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test2");
- assertEquals("test1,test3",
- Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+ Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+test4");
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test2"));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test3"));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test4"));
+ Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test3");
+ assertFalse(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test3"));
// Test deleting the last value in a 3 item list
- Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "test1,test2,test3");
- Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test3");
- assertEquals("test1,test2",
- Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+ Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+test5");
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test2"));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test4"));
+ assertTrue(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test5"));
+ Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test5");
+ assertFalse(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
+ .contains("test5"));
}
private boolean findUser(UserManager um, int userHandle) {
@@ -254,7 +282,7 @@
ContentResolver r = getContext().getContentResolver();
// Make sure there's an owner
- assertTrue(findUser(um, UserHandle.USER_OWNER));
+ assertTrue(findUser(um, UserHandle.USER_SYSTEM));
// create a new user to use for testing
UserInfo user = um.createUser("TestUser1", UserInfo.FLAG_GUEST);
@@ -266,12 +294,12 @@
final int SELF_VALUE = 40;
final int OTHER_VALUE = 27;
- Settings.System.putInt(r, TEST_KEY, SELF_VALUE);
- Settings.System.putIntForUser(r, TEST_KEY, OTHER_VALUE, user.id);
+ Settings.Secure.putInt(r, TEST_KEY, SELF_VALUE);
+ Settings.Secure.putIntForUser(r, TEST_KEY, OTHER_VALUE, user.id);
// Verify that they read back as intended
- int myValue = Settings.System.getInt(r, TEST_KEY, 0);
- int otherValue = Settings.System.getIntForUser(r, TEST_KEY, 0, user.id);
+ int myValue = Settings.Secure.getInt(r, TEST_KEY, 0);
+ int otherValue = Settings.Secure.getIntForUser(r, TEST_KEY, 0, user.id);
assertTrue("Running as user " + UserHandle.myUserId()
+ " and reading/writing as user " + user.id
+ ", expected to read " + SELF_VALUE + " but got " + myValue,
@@ -310,7 +338,8 @@
assertCanBeHandled(new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_PRIVACY_SETTINGS));
- assertCanBeHandled(new Intent(Settings.ACTION_QUICK_LAUNCH_SETTINGS));
+ //TODO: seems no one is using this anymore.
+// assertCanBeHandled(new Intent(Settings.ACTION_QUICK_LAUNCH_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_SEARCH_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_SECURITY_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_SETTINGS));
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/view/HandlerActionQueueTest.java b/core/tests/coretests/src/android/view/HandlerActionQueueTest.java
new file mode 100644
index 0000000..fd8f23a
--- /dev/null
+++ b/core/tests/coretests/src/android/view/HandlerActionQueueTest.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 android.view;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class HandlerActionQueueTest extends AndroidTestCase {
+
+ @SmallTest
+ public void testPostAndRemove() {
+ HandlerActionQueue runQueue = new HandlerActionQueue();
+ MockRunnable runnable1 = new MockRunnable();
+ MockRunnable runnable2 = new MockRunnable();
+ MockRunnable runnable3 = new MockRunnable();
+
+ runQueue.post(runnable1);
+ runQueue.post(runnable1);
+ runQueue.post(runnable2);
+ runQueue.postDelayed(runnable1, 100);
+ runQueue.postDelayed(null, 500);
+ assertEquals(5, runQueue.size());
+ assertEquals(0, runQueue.getDelay(0));
+ assertEquals(0, runQueue.getDelay(1));
+ assertEquals(0, runQueue.getDelay(2));
+ assertEquals(100, runQueue.getDelay(3));
+ assertEquals(500, runQueue.getDelay(4));
+ assertEquals(500, runQueue.getDelay(4));
+ assertEquals(runnable1, runQueue.getRunnable(0));
+ assertEquals(runnable1, runQueue.getRunnable(1));
+ assertEquals(runnable2, runQueue.getRunnable(2));
+ assertEquals(runnable1, runQueue.getRunnable(3));
+ assertEquals(null, runQueue.getRunnable(4));
+
+ runQueue.removeCallbacks(runnable1);
+ assertEquals(2, runQueue.size());
+ assertEquals(0, runQueue.getDelay(0));
+ assertEquals(500, runQueue.getDelay(1));
+ assertEquals(runnable2, runQueue.getRunnable(0));
+ assertEquals(null, runQueue.getRunnable(1));
+
+ try {
+ assertNull(runQueue.getRunnable(2));
+ assertFalse(true);
+ } catch (IndexOutOfBoundsException e) {
+ // Should throw an exception.
+ }
+
+ runQueue.removeCallbacks(runnable3);
+ assertEquals(2, runQueue.size());
+
+ runQueue.removeCallbacks(runnable2);
+ assertEquals(1, runQueue.size());
+ assertEquals(null, runQueue.getRunnable(0));
+
+ runQueue.removeCallbacks(null);
+ assertEquals(0, runQueue.size());
+ }
+
+ private static class MockRunnable implements Runnable {
+ @Override
+ public void run() {
+
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 54117df..b37688f 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,8 +16,10 @@
package android.widget;
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
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 +29,7 @@
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.view.KeyEvent;
/**
* Tests the TextView widget from an Activity
@@ -47,4 +50,17 @@
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(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!")));
+ }
}
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/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
new file mode 100644
index 0000000..425dccd
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -0,0 +1,105 @@
+/*
+ * 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.content.res.Resources;
+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.util.DisplayMetrics;
+import android.util.TypedValue;
+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 text view.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a text view displayed on screen
+ * <ul>
+ */
+ public static ViewAction clickOnTextAtIndex(int index) {
+ return actionWithAssertions(
+ new GeneralClickAction(Tap.SINGLE, new TextCoordinates(index), 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/listview/ListManagedCursorTest.java b/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java
index 7938cba..bc3776c 100644
--- a/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java
@@ -17,8 +17,7 @@
package android.widget.listview;
import android.app.Instrumentation;
-import android.test.ActivityInstrumentationTestCase;
-import android.test.FlakyTest;
+import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.view.KeyEvent;
@@ -28,12 +27,12 @@
/**
* Tests restoring the scroll position in a list with a managed cursor.
*/
-public class ListManagedCursorTest extends ActivityInstrumentationTestCase<ListManagedCursor> {
+public class ListManagedCursorTest extends ActivityInstrumentationTestCase2<ListManagedCursor> {
private ListManagedCursor mActivity;
private ListView mListView;
public ListManagedCursorTest() {
- super("com.android.frameworks.coretests", ListManagedCursor.class);
+ super(ListManagedCursor.class);
}
@Override
@@ -48,86 +47,46 @@
public void testPreconditions() {
assertNotNull(mActivity);
assertNotNull(mListView);
-
+
assertEquals(0, mListView.getFirstVisiblePosition());
}
-
+
/**
* Scroll the list using arrows, launch new activity, hit back, make sure we're still scrolled.
*/
@LargeTest
public void testKeyScrolling() {
Instrumentation inst = getInstrumentation();
-
+
int firstVisiblePosition = arrowScroll(inst);
-
+
inst.sendCharacterSync(KeyEvent.KEYCODE_BACK);
inst.waitForIdleSync();
-
- assertTrue("List changed to touch mode", !mListView.isInTouchMode());
- assertTrue("List did not preserve scroll position",
- firstVisiblePosition == mListView.getFirstVisiblePosition());
+
+ assertTrue("List changed to touch mode", !mListView.isInTouchMode());
+ assertTrue("List did not preserve scroll position",
+ firstVisiblePosition == mListView.getFirstVisiblePosition());
}
/**
- * Scroll the list using touch, launch new activity, hit back, make sure we're still scrolled.
- */
- @LargeTest
- public void testTouchScrolling() {
- Instrumentation inst = getInstrumentation();
-
- int firstVisiblePosition = touchScroll(inst);
-
- inst.sendCharacterSync(KeyEvent.KEYCODE_BACK);
- inst.waitForIdleSync();
-
- assertTrue("List not in touch mode", mListView.isInTouchMode());
- assertTrue("List did not preserve scroll position",
- firstVisiblePosition == mListView.getFirstVisiblePosition());
- }
-
- /**
* Scroll the list using arrows, launch new activity, change to touch mode, hit back, make sure
* we're still scrolled.
*/
@LargeTest
public void testKeyScrollingToTouchMode() {
Instrumentation inst = getInstrumentation();
-
+
int firstVisiblePosition = arrowScroll(inst);
-
- TouchUtils.dragQuarterScreenUp(this);
+
+ TouchUtils.dragQuarterScreenUp(this, getActivity());
inst.sendCharacterSync(KeyEvent.KEYCODE_BACK);
inst.waitForIdleSync();
-
- assertTrue("List did not change to touch mode", mListView.isInTouchMode());
- assertTrue("List did not preserve scroll position",
- firstVisiblePosition == mListView.getFirstVisiblePosition());
+
+ assertTrue("List did not change to touch mode", mListView.isInTouchMode());
+ assertTrue("List did not preserve scroll position",
+ firstVisiblePosition == mListView.getFirstVisiblePosition());
}
-
- /**
- * Scroll the list using touch, launch new activity, change to trackball mode, hit back, make
- * sure we're still scrolled.
- */
- @FlakyTest(tolerance=3)
- @LargeTest
- public void testTouchScrollingToTrackballMode() {
- Instrumentation inst = getInstrumentation();
-
- int firstVisiblePosition = touchScroll(inst);
-
- inst.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
- inst.waitForIdleSync();
- inst.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
- inst.waitForIdleSync();
- inst.sendCharacterSync(KeyEvent.KEYCODE_BACK);
- inst.waitForIdleSync();
- assertTrue("List not in trackball mode", !mListView.isInTouchMode());
- assertTrue("List did not preserve scroll position", firstVisiblePosition == mListView
- .getFirstVisiblePosition());
- }
-
public int arrowScroll(Instrumentation inst) {
int count = mListView.getChildCount();
@@ -151,30 +110,4 @@
return firstVisiblePosition;
}
-
- public int touchScroll(Instrumentation inst) {
- TouchUtils.dragQuarterScreenUp(this);
- inst.waitForIdleSync();
- TouchUtils.dragQuarterScreenUp(this);
- inst.waitForIdleSync();
- TouchUtils.dragQuarterScreenUp(this);
- inst.waitForIdleSync();
- TouchUtils.dragQuarterScreenUp(this);
- inst.waitForIdleSync();
-
- int firstVisiblePosition = mListView.getFirstVisiblePosition();
- assertTrue("Touch scroll did not happen", firstVisiblePosition > 0);
- assertTrue("List not in touch mode", mListView.isInTouchMode());
-
- TouchUtils.clickView(this, mListView.getChildAt(mListView.getChildCount() - 1));
- inst.waitForIdleSync();
-
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- return firstVisiblePosition;
- }
}
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/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/training/auto/start/index.jd b/docs/html/training/auto/start/index.jd
index f6cdbd1..6c6f188 100644
--- a/docs/html/training/auto/start/index.jd
+++ b/docs/html/training/auto/start/index.jd
@@ -177,7 +177,7 @@
href="https://play.google.com/store/apps/details?id=com.google.android.projection.gearhead&hl=en"
>Android Auto app</a> on the mobile device.</li>
<li>Open the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and
- download the DHU package <strong>Android Auto Desktop Head Unit</strong> from the
+ download the DHU package <strong>Android Auto Desktop Head Unit emulator</strong> from the
<em>SDK Tools</em> tab. The DHU installs in the <code><sdk>/extras/google/auto/</code>
directory.</li>
<li>If you are running the DHU on Linux, you must also install
diff --git a/docs/html/training/building-userinfo.jd b/docs/html/training/building-userinfo.jd
index f9d77f7..40e5b94 100644
--- a/docs/html/training/building-userinfo.jd
+++ b/docs/html/training/building-userinfo.jd
@@ -1,9 +1,9 @@
-page.title=Building Apps with User Info & Location
+page.title=Building Apps with Contacts & Sign-In
page.trainingcourse=true
@jd:body
-<p>These classes teach you how to add user personalization to your app. Some of the ways
-you can do this is by identifying users, providing
-information that's relevant to them, and providing information about the world around them.</p>
\ No newline at end of file
+<p>These lessons teach you how to include contact information and authenticate users with the same
+credentials they use for Google. These features allow your app to connect users with people they
+care about and provide a personalized experience without creating new user accounts.</p>
diff --git a/docs/html/training/contacts-provider/retrieve-names.jd b/docs/html/training/contacts-provider/retrieve-names.jd
index 7106889a..d97b81b 100644
--- a/docs/html/training/contacts-provider/retrieve-names.jd
+++ b/docs/html/training/contacts-provider/retrieve-names.jd
@@ -731,7 +731,6 @@
Define ListView and item layouts.
</li>
<li>
- <li>
Define a Fragment that displays the list of contacts.
</li>
<li>
diff --git a/docs/html/training/sign-in/index.jd b/docs/html/training/sign-in/index.jd
index 9d49fd9..d7c8e1d 100644
--- a/docs/html/training/sign-in/index.jd
+++ b/docs/html/training/sign-in/index.jd
@@ -1,5 +1,5 @@
page.title=Adding Sign-In
-page.tags=authentication,signin,social,google+
+page.tags=authentication,signin
page.article=true
page.trainingcourse=true
@jd:body
@@ -11,13 +11,13 @@
alt="Google maps sample image">
<p>
- The Google+ platform for Android lets you authenticate a user with the same credentials they use
- on Google every day. Once a user signs in with Google, you can create more engaging experiences
- and drive usage of your app.
+ Google Sign-In for Android lets you authenticate a user with the same credentials they use on
+ Google. After a user signs in with Google, you can create more engaging experiences and drive
+ usage of your app.
</p>
<p>
- The <a href="https://developers.google.com/+/mobile/android/">Google+ Android API</a> allows
+ The <a href="https://developers.google.com/identity/sign-in/android/">Google Android API</a> allows
you to integrate sign-in and social features into your app.
</p>
@@ -26,43 +26,27 @@
<h4>Trusted authentication</h4>
<p>
- Google+ Sign-In is a simple, trusted, and secure way to let people sign in to your app with their
- Google credentials and bring along their Google+ info.<br>
- <a href="https://developers.google.com/+/mobile/android/sign-in" class="external-link">Add
- sign-in</a>.
+ Google Sign-In is a simple, trusted, and secure way to let people sign in to your app with their
+ Google credentials.<br>
+ <a href="https://developers.google.com/identity/sign-in/android/sign-in" class="external-link">Add
+ Sign-in</a>.
</p>
<h4>Access the profile and social graph</h4>
<p>
- Once users have signed in with Google, your app can welcome them by name, display their picture,
- connect them with friends, and lots more.<br>
- <a href="https://developers.google.com/+/mobile/android/people" class="external-link">Access the
- social graph</a>.
-</p>
-
-<h4>Stand out in the stream</h4>
-<p>
- Interactive posts is a rich way of sharing to Google+. It lets users prompt friends to take
- specific actions in your app from a Google+ post, like "listen," "RSVP," "check-in," and over 100
- more actions.<br>
- <a class="external-link" href="https://developers.google.com/+/mobile/android/share">Post
- interactive content</a>.
-</p>
-
-<h4>Recommend content</h4>
-<p>
- Add a native +1 button so users can recommend content from your app. These endorsements can give
- your app more credibility and help it grow faster.<br>
- <a class="external-link" href="https://developers.google.com/+/mobile/android/recommend">Add the
- +1 button</a>.
+ After users have signed in with Google, your app can welcome them by name and display their
+ picture. If your app requests social scopes, it can connect users with friends, and access
+ age range, language, and public profile information.<br>
+ <a href="https://developers.google.com/identity/sign-in/android/people" class="external-link">
+ Getting Profile Information</a>.
</p>
<h2 id="start">Get Started</h2>
<p>
- The Google+ Android APIs are part of the Google Play services platform. To use Google+ features,
+ The Google Android APIs are part of the Google Play services platform. To use Google features,
set up the Google Play services SDK in your app development project. For more information, see
the <a class="external-link" href=
- "https://developers.google.com/+/mobile/android/getting-started">Getting Started</a> guide for
- the Google+ Platform for Android
+ "https://developers.google.com/identity/sign-in/android/start-integrating">Start Integrating</a>
+ guide for Google Sign-In.
</p>
\ No newline at end of file
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 7cffdd8..2963345 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -811,7 +811,7 @@
<div class="nav-section-header">
<a href="<?cs var:toroot ?>training/building-userinfo.html">
<span class="small">Building Apps with</span><br/>
- User Info & Sign-In
+ Contacts & Sign-In
</a>
</div>
<ul>
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 c5d68bd..6582b7e 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -17,10 +17,13 @@
package android.graphics;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Size;
import android.text.GraphicsOperations;
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
+import android.util.LocaleList;
import java.util.Locale;
@@ -50,7 +53,7 @@
private float mCompatScaling;
private float mInvCompatScaling;
- private Locale mLocale;
+ private LocaleList mLocales;
private String mFontFeatureSettings;
/**
@@ -434,7 +437,7 @@
// setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
// ? HINTING_OFF : HINTING_ON);
mCompatScaling = mInvCompatScaling = 1;
- setTextLocale(Locale.getDefault());
+ setTextLocales(LocaleList.getDefault());
}
/**
@@ -474,7 +477,7 @@
mInvCompatScaling = 1;
mBidiFlags = BIDI_DEFAULT_LTR;
- setTextLocale(Locale.getDefault());
+ setTextLocales(LocaleList.getDefault());
setElegantTextHeight(false);
mFontFeatureSettings = null;
}
@@ -512,7 +515,7 @@
mInvCompatScaling = paint.mInvCompatScaling;
mBidiFlags = paint.mBidiFlags;
- mLocale = paint.mLocale;
+ mLocales = paint.mLocales;
mFontFeatureSettings = paint.mFontFeatureSettings;
}
@@ -1174,47 +1177,80 @@
}
/**
- * Get the text Locale.
+ * Get the text's primary Locale. Note that this is not all of the locale-related information
+ * Paint has. Use {@link #getTextLocales()} to get the complete list.
*
- * @return the paint's Locale used for drawing text, never null.
+ * @return the paint's primary Locale used for drawing text, never null.
*/
+ @NonNull
public Locale getTextLocale() {
- return mLocale;
+ return mLocales.getPrimary();
}
/**
- * Set the text locale.
+ * Get the text locale list.
*
- * The text locale affects how the text is drawn for some languages.
+ * @return the paint's LocaleList used for drawing text, never null or empty.
+ */
+ @NonNull @Size(min=1)
+ public LocaleList getTextLocales() {
+ return mLocales;
+ }
+
+ /**
+ * Set the text locale list to a one-member list consisting of just the locale.
*
- * For example, if the locale is {@link Locale#CHINESE} or {@link Locale#CHINA},
+ * See {@link #setTextLocales(LocaleList)} for how the locale list affects
+ * the way the text is drawn for some languages.
+ *
+ * @param locale the paint's locale value for drawing text, must not be null.
+ */
+ public void setTextLocale(@NonNull Locale locale) {
+ if (locale == null) {
+ throw new IllegalArgumentException("locale cannot be null");
+ }
+ if (mLocales != null && mLocales.size() == 1 && locale.equals(mLocales.getPrimary())) {
+ return;
+ }
+ mLocales = new LocaleList(locale);
+ native_setTextLocale(mNativePaint, locale.toString());
+ }
+
+ /**
+ * Set the text locale list.
+ *
+ * The text locale list affects how the text is drawn for some languages.
+ *
+ * For example, if the locale list contains {@link Locale#CHINESE} or {@link Locale#CHINA},
* then the text renderer will prefer to draw text using a Chinese font. Likewise,
- * if the locale is {@link Locale#JAPANESE} or {@link Locale#JAPAN}, then the text
- * renderer will prefer to draw text using a Japanese font.
+ * if the locale list contains {@link Locale#JAPANESE} or {@link Locale#JAPAN}, then the text
+ * renderer will prefer to draw text using a Japanese font. If the locale list contains both,
+ * the order those locales appear in the list is considered for deciding the font.
*
* This distinction is important because Chinese and Japanese text both use many
* of the same Unicode code points but their appearance is subtly different for
* each language.
*
- * By default, the text locale is initialized to the system locale (as returned
- * by {@link Locale#getDefault}). This assumes that the text to be rendered will
- * most likely be in the user's preferred language.
+ * By default, the text locale list is initialized to a one-member list just containing the
+ * system locale (as returned by {@link LocaleList#getDefault()}). This assumes that the text to
+ * be rendered will most likely be in the user's preferred language.
*
- * If the actual language of the text is known, then it can be provided to the
- * text renderer using this method. The text renderer may attempt to guess the
+ * If the actual language or languages of the text is/are known, then they can be provided to
+ * the text renderer using this method. The text renderer may attempt to guess the
* language script based on the contents of the text to be drawn independent of
- * the text locale here. Specifying the text locale just helps it do a better
- * job in certain ambiguous cases
+ * the text locale here. Specifying the text locales just helps it do a better
+ * job in certain ambiguous cases.
*
- * @param locale the paint's locale value for drawing text, must not be null.
+ * @param locales the paint's locale list for drawing text, must not be null or empty.
*/
- public void setTextLocale(Locale locale) {
- if (locale == null) {
- throw new IllegalArgumentException("locale cannot be null");
+ public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
+ if (locales == null || locales.isEmpty()) {
+ throw new IllegalArgumentException("locales cannot be null or empty");
}
- if (locale.equals(mLocale)) return;
- mLocale = locale;
- native_setTextLocale(mNativePaint, locale.toString());
+ if (locales.equals(mLocales)) return;
+ mLocales = locales;
+ // TODO: Pass the whole LocaleList to native code
+ native_setTextLocale(mNativePaint, locales.getPrimary().toString());
}
/**
@@ -2435,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/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index e1975c9..521c74b 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -105,10 +105,12 @@
/**
* Sets whether this AnimationDrawable is visible.
* <p>
- * When the drawable becomes invisible, it will pause its animation. A
- * subsequent change to visible with <code>restart</code> set to true will
- * restart the animation from the first frame. If <code>restart</code> is
- * false, the animation will resume from the most recent frame.
+ * When the drawable becomes invisible, it will pause its animation. A subsequent change to
+ * visible with <code>restart</code> set to true will restart the animation from the
+ * first frame. If <code>restart</code> is false, the drawable will resume from the most recent
+ * frame. If the drawable has already reached the last frame, it will then loop back to the
+ * first frame, unless it's a one shot drawable (set through {@link #setOneShot(boolean)}),
+ * in which case, it will stay on the last frame.
*
* @param visible true if visible, false otherwise
* @param restart when visible, true to force the animation to restart
@@ -120,7 +122,7 @@
final boolean changed = super.setVisible(visible, restart);
if (visible) {
if (restart || changed) {
- boolean startFromZero = restart || !mRunning ||
+ boolean startFromZero = restart || (!mRunning && !mAnimationState.mOneShot) ||
mCurFrame >= mAnimationState.getChildCount();
setFrame(startFromZero ? 0 : mCurFrame, true, mAnimating);
}
@@ -131,7 +133,7 @@
}
/**
- * Starts the animation, looping if necessary. This method has no effect
+ * Starts the animation from the first frame, looping if necessary. This method has no effect
* if the animation is running.
* <p>
* <strong>Note:</strong> Do not call this in the
@@ -158,7 +160,7 @@
}
/**
- * Stops the animation. This method has no effect if the animation is not
+ * Stops the animation at the current frame. This method has no effect if the animation is not
* running.
*
* @see #isRunning()
@@ -169,6 +171,7 @@
mAnimating = false;
if (isRunning()) {
+ mCurFrame = 0;
unscheduleSelf(this);
}
}
@@ -196,7 +199,6 @@
@Override
public void unscheduleSelf(Runnable what) {
- mCurFrame = 0;
mRunning = false;
super.unscheduleSelf(what);
}
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 1915dd7..1f7d996 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -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;
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 1cfccc4..1747225 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -726,7 +726,7 @@
Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString());
// Then print all the children groups
for (int i = 0; i < currentGroup.mChildren.size(); i++) {
- Object child = currentGroup.mChildren.get(i);
+ final VObject child = currentGroup.mChildren.get(i);
if (child instanceof VGroup) {
printGroupTree((VGroup) child, level + 1);
}
@@ -783,12 +783,6 @@
mThemeAttrs = copy.mThemeAttrs;
mChangingConfigurations = copy.mChangingConfigurations;
mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
- if (copy.mVPathRenderer.mFillPaint != null) {
- mVPathRenderer.mFillPaint = new Paint(copy.mVPathRenderer.mFillPaint);
- }
- if (copy.mVPathRenderer.mStrokePaint != null) {
- mVPathRenderer.mStrokePaint = new Paint(copy.mVPathRenderer.mStrokePaint);
- }
mTint = copy.mTint;
mTintMode = copy.mTintMode;
mAutoMirrored = copy.mAutoMirrored;
@@ -913,13 +907,7 @@
*/
// Variables that only used temporarily inside the draw() call, so there
// is no need for deep copying.
- private final Path mPath;
- private final Path mRenderPath;
- private final Matrix mFinalPathMatrix = new Matrix();
-
- private Paint mStrokePaint;
- private Paint mFillPaint;
- private PathMeasure mPathMeasure;
+ private final TempState mTempState = new TempState();
/////////////////////////////////////////////////////
// Variables below need to be copied (deep copy if applicable) for mutation.
@@ -935,12 +923,10 @@
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
- final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>();
+ final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
public VPathRenderer() {
mRootGroup = new VGroup();
- mPath = new Path();
- mRenderPath = new Path();
}
public void setRootAlpha(int alpha) {
@@ -964,8 +950,6 @@
public VPathRenderer(VPathRenderer copy) {
mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
- mPath = new Path(copy.mPath);
- mRenderPath = new Path(copy.mRenderPath);
mBaseWidth = copy.mBaseWidth;
mBaseHeight = copy.mBaseHeight;
mViewportWidth = copy.mViewportWidth;
@@ -981,215 +965,28 @@
}
public boolean canApplyTheme() {
- // If one of the paths can apply theme, then return true;
- return recursiveCanApplyTheme(mRootGroup);
- }
-
- private boolean recursiveCanApplyTheme(VGroup currentGroup) {
- // We can do a tree traverse here, if there is one path return true,
- // then we return true for the whole tree.
- final ArrayList<Object> children = currentGroup.mChildren;
-
- for (int i = 0; i < children.size(); i++) {
- Object child = children.get(i);
- if (child instanceof VGroup) {
- VGroup childGroup = (VGroup) child;
- if (childGroup.canApplyTheme()
- || recursiveCanApplyTheme(childGroup)) {
- return true;
- }
- } else if (child instanceof VPath) {
- VPath childPath = (VPath) child;
- if (childPath.canApplyTheme()) {
- return true;
- }
- }
- }
- return false;
+ return mRootGroup.canApplyTheme();
}
public void applyTheme(Theme t) {
- // Apply theme to every path of the tree.
- recursiveApplyTheme(mRootGroup, t);
- }
-
- private void recursiveApplyTheme(VGroup currentGroup, Theme t) {
- // We can do a tree traverse here, apply theme to all paths which
- // can apply theme.
- final ArrayList<Object> children = currentGroup.mChildren;
- for (int i = 0; i < children.size(); i++) {
- Object child = children.get(i);
- if (child instanceof VGroup) {
- VGroup childGroup = (VGroup) child;
- if (childGroup.canApplyTheme()) {
- childGroup.applyTheme(t);
- }
- recursiveApplyTheme(childGroup, t);
- } else if (child instanceof VPath) {
- VPath childPath = (VPath) child;
- if (childPath.canApplyTheme()) {
- childPath.applyTheme(t);
- }
- }
- }
- }
-
- private void drawGroupTree(VGroup currentGroup, Matrix currentMatrix,
- Canvas canvas, int w, int h, ColorFilter filter) {
- // Calculate current group's matrix by preConcat the parent's and
- // and the current one on the top of the stack.
- // Basically the Mfinal = Mviewport * M0 * M1 * M2;
- // Mi the local matrix at level i of the group tree.
- currentGroup.mStackedMatrix.set(currentMatrix);
- currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix);
-
- // Save the current clip information, which is local to this group.
- canvas.save();
- // Draw the group tree in the same order as the XML file.
- for (int i = 0; i < currentGroup.mChildren.size(); i++) {
- Object child = currentGroup.mChildren.get(i);
- if (child instanceof VGroup) {
- VGroup childGroup = (VGroup) child;
- drawGroupTree(childGroup, currentGroup.mStackedMatrix,
- canvas, w, h, filter);
- } else if (child instanceof VPath) {
- VPath childPath = (VPath) child;
- drawPath(currentGroup, childPath, canvas, w, h, filter);
- }
- }
- canvas.restore();
+ mRootGroup.applyTheme(t);
}
public void draw(Canvas canvas, int w, int h, ColorFilter filter) {
- // Travese the tree in pre-order to draw.
- drawGroupTree(mRootGroup, Matrix.IDENTITY_MATRIX, canvas, w, h, filter);
- }
-
- private void drawPath(VGroup vGroup, VPath vPath, Canvas canvas, int w, int h,
- ColorFilter filter) {
final float scaleX = w / mViewportWidth;
final float scaleY = h / mViewportHeight;
- final float minScale = Math.min(scaleX, scaleY);
- final Matrix groupStackedMatrix = vGroup.mStackedMatrix;
-
- mFinalPathMatrix.set(groupStackedMatrix);
- mFinalPathMatrix.postScale(scaleX, scaleY);
-
- final float matrixScale = getMatrixScale(groupStackedMatrix);
- if (matrixScale == 0) {
- // When either x or y is scaled to 0, we don't need to draw anything.
- return;
- }
- vPath.toPath(mPath);
- final Path path = mPath;
-
- mRenderPath.reset();
-
- if (vPath.isClipPath()) {
- mRenderPath.addPath(path, mFinalPathMatrix);
- canvas.clipPath(mRenderPath);
- } else {
- VFullPath fullPath = (VFullPath) vPath;
- if (fullPath.mTrimPathStart != 0.0f || fullPath.mTrimPathEnd != 1.0f) {
- float start = (fullPath.mTrimPathStart + fullPath.mTrimPathOffset) % 1.0f;
- float end = (fullPath.mTrimPathEnd + fullPath.mTrimPathOffset) % 1.0f;
-
- if (mPathMeasure == null) {
- mPathMeasure = new PathMeasure();
- }
- mPathMeasure.setPath(mPath, false);
-
- float len = mPathMeasure.getLength();
- start = start * len;
- end = end * len;
- path.reset();
- if (start > end) {
- mPathMeasure.getSegment(start, len, path, true);
- mPathMeasure.getSegment(0f, end, path, true);
- } else {
- mPathMeasure.getSegment(start, end, path, true);
- }
- path.rLineTo(0, 0); // fix bug in measure
- }
- mRenderPath.addPath(path, mFinalPathMatrix);
-
- if (fullPath.mFillColor != Color.TRANSPARENT) {
- if (mFillPaint == null) {
- mFillPaint = new Paint();
- mFillPaint.setStyle(Paint.Style.FILL);
- mFillPaint.setAntiAlias(true);
- }
-
- final Paint fillPaint = mFillPaint;
- fillPaint.setColor(applyAlpha(fullPath.mFillColor, fullPath.mFillAlpha));
- fillPaint.setColorFilter(filter);
- canvas.drawPath(mRenderPath, fillPaint);
- }
-
- if (fullPath.mStrokeColor != Color.TRANSPARENT) {
- if (mStrokePaint == null) {
- mStrokePaint = new Paint();
- mStrokePaint.setStyle(Paint.Style.STROKE);
- mStrokePaint.setAntiAlias(true);
- }
-
- final Paint strokePaint = mStrokePaint;
- if (fullPath.mStrokeLineJoin != null) {
- strokePaint.setStrokeJoin(fullPath.mStrokeLineJoin);
- }
-
- if (fullPath.mStrokeLineCap != null) {
- strokePaint.setStrokeCap(fullPath.mStrokeLineCap);
- }
-
- strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit);
- strokePaint.setColor(applyAlpha(fullPath.mStrokeColor, fullPath.mStrokeAlpha));
- strokePaint.setColorFilter(filter);
- final float finalStrokeScale = minScale * matrixScale;
- strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
- canvas.drawPath(mRenderPath, strokePaint);
- }
- }
- }
-
- private float getMatrixScale(Matrix groupStackedMatrix) {
- // Given unit vectors A = (0, 1) and B = (1, 0).
- // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'.
- // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)),
- // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|);
- // If max (|A'|, |B'|) = 0, that means either x or y has a scale of 0.
- //
- // For non-skew case, which is most of the cases, matrix scale is computing exactly the
- // scale on x and y axis, and take the minimal of these two.
- // For skew case, an unit square will mapped to a parallelogram. And this function will
- // return the minimal height of the 2 bases.
- float[] unitVectors = new float[] {0, 1, 1, 0};
- groupStackedMatrix.mapVectors(unitVectors);
- float scaleX = MathUtils.mag(unitVectors[0], unitVectors[1]);
- float scaleY = MathUtils.mag(unitVectors[2], unitVectors[3]);
- float crossProduct = MathUtils.cross(unitVectors[0], unitVectors[1],
- unitVectors[2], unitVectors[3]);
- float maxScale = MathUtils.max(scaleX, scaleY);
-
- float matrixScale = 0;
- if (maxScale > 0) {
- matrixScale = MathUtils.abs(crossProduct) / maxScale;
- }
- if (DBG_VECTOR_DRAWABLE) {
- Log.d(LOGTAG, "Scale x " + scaleX + " y " + scaleY + " final " + matrixScale);
- }
- return matrixScale;
+ mRootGroup.draw(canvas, mTempState, Matrix.IDENTITY_MATRIX, filter, scaleX, scaleY);
}
}
- private static class VGroup {
+ private static class VGroup implements VObject {
// 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<Object> mChildren = new ArrayList<Object>();
+ final ArrayList<VObject> mChildren = new ArrayList<>();
private float mRotate = 0;
private float mPivotX = 0;
@@ -1223,14 +1020,14 @@
mLocalMatrix.set(copy.mLocalMatrix);
- final ArrayList<Object> children = copy.mChildren;
+ final ArrayList<VObject> children = copy.mChildren;
for (int i = 0; i < children.size(); i++) {
- Object copyChild = children.get(i);
+ final VObject copyChild = children.get(i);
if (copyChild instanceof VGroup) {
- VGroup copyGroup = (VGroup) copyChild;
+ final VGroup copyGroup = (VGroup) copyChild;
mChildren.add(new VGroup(copyGroup, targetsMap));
} else {
- VPath newPath = null;
+ final VPath newPath;
if (copyChild instanceof VFullPath) {
newPath = new VFullPath((VFullPath) copyChild);
} else if (copyChild instanceof VClipPath) {
@@ -1257,6 +1054,30 @@
return mLocalMatrix;
}
+ @Override
+ public void draw(Canvas canvas, TempState temp, Matrix currentMatrix,
+ ColorFilter filter, float scaleX, float scaleY) {
+ // Calculate current group's matrix by preConcat the parent's and
+ // and the current one on the top of the stack.
+ // Basically the Mfinal = Mviewport * M0 * M1 * M2;
+ // Mi the local matrix at level i of the group tree.
+ mStackedMatrix.set(currentMatrix);
+ mStackedMatrix.preConcat(mLocalMatrix);
+
+ // Save the current clip information, which is local to this group.
+ canvas.save();
+
+ // Draw the group tree in the same order as the XML file.
+ for (int i = 0, count = mChildren.size(); i < count; i++) {
+ final VObject child = mChildren.get(i);
+ child.draw(canvas, temp, mStackedMatrix, filter, scaleX, scaleY);
+ }
+
+ // Restore the previous clip information.
+ canvas.restore();
+ }
+
+ @Override
public void inflate(Resources res, AttributeSet attrs, Theme theme) {
final TypedArray a = obtainAttributes(res, theme, attrs,
R.styleable.VectorDrawableGroup);
@@ -1287,18 +1108,39 @@
updateLocalMatrix();
}
+ @Override
public boolean canApplyTheme() {
- return mThemeAttrs != null;
- }
-
- public void applyTheme(Theme t) {
- if (mThemeAttrs == null) {
- return;
+ if (mThemeAttrs != null) {
+ return true;
}
- final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawableGroup);
- updateStateFromTypedArray(a);
- a.recycle();
+ final ArrayList<VObject> children = mChildren;
+ for (int i = 0, count = children.size(); i < count; i++) {
+ final VObject child = children.get(i);
+ if (child.canApplyTheme()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ if (mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(mThemeAttrs,
+ R.styleable.VectorDrawableGroup);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+
+ final ArrayList<VObject> children = mChildren;
+ for (int i = 0, count = children.size(); i < count; i++) {
+ final VObject child = children.get(i);
+ if (child.canApplyTheme()) {
+ child.applyTheme(t);
+ }
+ }
}
private void updateLocalMatrix() {
@@ -1407,7 +1249,7 @@
/**
* Common Path information for clip path and normal path.
*/
- private static class VPath {
+ private static abstract class VPath implements VObject {
protected PathParser.PathDataNode[] mNodes = null;
String mPathName;
int mChangingConfigurations;
@@ -1422,24 +1264,10 @@
mNodes = PathParser.deepCopyNodes(copy.mNodes);
}
- public void toPath(Path path) {
- path.reset();
- if (mNodes != null) {
- PathParser.PathDataNode.nodesToPath(mNodes, path);
- }
- }
-
public String getPathName() {
return mPathName;
}
- public boolean canApplyTheme() {
- return false;
- }
-
- public void applyTheme(Theme t) {
- }
-
public boolean isClipPath() {
return false;
}
@@ -1459,6 +1287,79 @@
PathParser.updateNodes(mNodes, nodes);
}
}
+
+ @Override
+ public final void draw(Canvas canvas, TempState temp, Matrix groupStackedMatrix,
+ ColorFilter filter, float scaleX, float scaleY) {
+ final float matrixScale = VPath.getMatrixScale(groupStackedMatrix);
+ if (matrixScale == 0) {
+ // When either x or y is scaled to 0, we don't need to draw anything.
+ return;
+ }
+
+ final Path path = temp.path;
+ path.reset();
+ toPath(temp, path);
+
+ final Matrix pathMatrix = temp.pathMatrix;
+ pathMatrix.set(groupStackedMatrix);
+ pathMatrix.postScale(scaleX, scaleY);
+
+ final Path renderPath = temp.renderPath;
+ renderPath.reset();
+ renderPath.addPath(path, pathMatrix);
+
+ final float minScale = Math.min(scaleX, scaleY);
+ final float strokeScale = minScale * matrixScale;
+ drawPath(temp, renderPath, canvas, filter, strokeScale);
+ }
+
+ /**
+ * Writes the path's nodes to an output Path for rendering.
+ *
+ * @param temp temporary state variables
+ * @param outPath the output path
+ */
+ protected void toPath(TempState temp, Path outPath) {
+ if (mNodes != null) {
+ PathParser.PathDataNode.nodesToPath(mNodes, outPath);
+ }
+ }
+
+ /**
+ * Draws the specified path into the supplied canvas.
+ */
+ protected abstract void drawPath(TempState temp, Path path, Canvas canvas,
+ ColorFilter filter, float strokeScale);
+
+ private static float getMatrixScale(Matrix groupStackedMatrix) {
+ // Given unit vectors A = (0, 1) and B = (1, 0).
+ // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'.
+ // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)),
+ // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|);
+ // If max (|A'|, |B'|) = 0, that means either x or y has a scale of 0.
+ //
+ // For non-skew case, which is most of the cases, matrix scale is computing exactly the
+ // scale on x and y axis, and take the minimal of these two.
+ // For skew case, an unit square will mapped to a parallelogram. And this function will
+ // return the minimal height of the 2 bases.
+ float[] unitVectors = new float[] {0, 1, 1, 0};
+ groupStackedMatrix.mapVectors(unitVectors);
+ float scaleX = MathUtils.mag(unitVectors[0], unitVectors[1]);
+ float scaleY = MathUtils.mag(unitVectors[2], unitVectors[3]);
+ float crossProduct = MathUtils.cross(unitVectors[0], unitVectors[1],
+ unitVectors[2], unitVectors[3]);
+ float maxScale = MathUtils.max(scaleX, scaleY);
+
+ float matrixScale = 0;
+ if (maxScale > 0) {
+ matrixScale = MathUtils.abs(crossProduct) / maxScale;
+ }
+ if (DBG_VECTOR_DRAWABLE) {
+ Log.d(LOGTAG, "Scale x " + scaleX + " y " + scaleY + " final " + matrixScale);
+ }
+ return matrixScale;
+ }
}
/**
@@ -1473,6 +1374,13 @@
super(copy);
}
+ @Override
+ protected void drawPath(TempState temp, Path renderPath, Canvas canvas, ColorFilter filter,
+ float strokeScale) {
+ canvas.clipPath(renderPath);
+ }
+
+ @Override
public void inflate(Resources r, AttributeSet attrs, Theme theme) {
final TypedArray a = obtainAttributes(r, theme, attrs,
R.styleable.VectorDrawableClipPath);
@@ -1480,6 +1388,16 @@
a.recycle();
}
+ @Override
+ public boolean canApplyTheme() {
+ return false;
+ }
+
+ @Override
+ public void applyTheme(Theme theme) {
+ // No-op.
+ }
+
private void updateStateFromTypedArray(TypedArray a) {
// Account for any configuration changes.
mChangingConfigurations |= a.getChangingConfigurations();
@@ -1574,10 +1492,104 @@
}
@Override
- public boolean canApplyTheme() {
- return mThemeAttrs != null;
+ public void toPath(TempState temp, Path path) {
+ super.toPath(temp, path);
+
+ if (mTrimPathStart != 0.0f || mTrimPathEnd != 1.0f) {
+ VFullPath.applyTrim(temp, path, mTrimPathStart, mTrimPathEnd, mTrimPathOffset);
+ }
}
+ @Override
+ protected void drawPath(TempState temp, Path path, Canvas canvas, ColorFilter filter,
+ float strokeScale) {
+ drawPathFill(temp, path, canvas, filter);
+ drawPathStroke(temp, path, canvas, filter, strokeScale);
+ }
+
+ /**
+ * Draws this path's fill, if necessary.
+ */
+ private void drawPathFill(TempState temp, Path path, Canvas canvas, ColorFilter filter) {
+ if (mFillColor == Color.TRANSPARENT) {
+ return;
+ }
+
+ if (temp.mFillPaint == null) {
+ temp.mFillPaint = new Paint();
+ temp.mFillPaint.setStyle(Paint.Style.FILL);
+ temp.mFillPaint.setAntiAlias(true);
+ }
+
+ final Paint fillPaint = temp.mFillPaint;
+ fillPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
+ fillPaint.setColorFilter(filter);
+ canvas.drawPath(path, fillPaint);
+ }
+
+ /**
+ * Draws this path's stroke, if necessary.
+ */
+ private void drawPathStroke(TempState temp, Path path, Canvas canvas, ColorFilter filter,
+ float strokeScale) {
+ if (mStrokeColor == Color.TRANSPARENT) {
+ return;
+ }
+
+ if (temp.mStrokePaint == null) {
+ temp.mStrokePaint = new Paint();
+ temp.mStrokePaint.setStyle(Paint.Style.STROKE);
+ temp.mStrokePaint.setAntiAlias(true);
+ }
+
+ final Paint strokePaint = temp.mStrokePaint;
+ if (mStrokeLineJoin != null) {
+ strokePaint.setStrokeJoin(mStrokeLineJoin);
+ }
+
+ if (mStrokeLineCap != null) {
+ strokePaint.setStrokeCap(mStrokeLineCap);
+ }
+
+ strokePaint.setStrokeMiter(mStrokeMiterlimit);
+ strokePaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
+ strokePaint.setColorFilter(filter);
+ strokePaint.setStrokeWidth(mStrokeWidth * strokeScale);
+ canvas.drawPath(path, strokePaint);
+ }
+
+ /**
+ * Applies trimming to the specified path.
+ */
+ private static void applyTrim(TempState temp, Path path, float mTrimPathStart,
+ float mTrimPathEnd, float mTrimPathOffset) {
+ if (mTrimPathStart == 0.0f && mTrimPathEnd == 1.0f) {
+ // No trimming necessary.
+ return;
+ }
+
+ if (temp.mPathMeasure == null) {
+ temp.mPathMeasure = new PathMeasure();
+ }
+ final PathMeasure pathMeasure = temp.mPathMeasure;
+ pathMeasure.setPath(path, false);
+
+ final float len = pathMeasure.getLength();
+ final float start = len * ((mTrimPathStart + mTrimPathOffset) % 1.0f);
+ final float end = len * ((mTrimPathEnd + mTrimPathOffset) % 1.0f);
+ path.reset();
+ if (start > end) {
+ pathMeasure.getSegment(start, len, path, true);
+ pathMeasure.getSegment(0, end, path, true);
+ } else {
+ pathMeasure.getSegment(start, end, path, true);
+ }
+
+ // Fix bug in measure.
+ path.rLineTo(0, 0);
+ }
+
+ @Override
public void inflate(Resources r, AttributeSet attrs, Theme theme) {
final TypedArray a = obtainAttributes(r, theme, attrs,
R.styleable.VectorDrawablePath);
@@ -1627,6 +1639,11 @@
}
@Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
+ @Override
public void applyTheme(Theme t) {
if (mThemeAttrs == null) {
return;
@@ -1718,4 +1735,22 @@
mTrimPathOffset = trimPathOffset;
}
}
+
+ static class TempState {
+ final Matrix pathMatrix = new Matrix();
+ final Path path = new Path();
+ final Path renderPath = new Path();
+
+ PathMeasure mPathMeasure;
+ Paint mFillPaint;
+ Paint mStrokePaint;
+ }
+
+ interface VObject {
+ void draw(Canvas canvas, TempState temp, Matrix currentMatrix,
+ ColorFilter filter, float scaleX, float scaleY);
+ void inflate(Resources r, AttributeSet attrs, Theme theme);
+ boolean canApplyTheme();
+ void applyTheme(Theme t);
+ }
}
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index 0cfd2b1..3d4e47d 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -93,13 +93,14 @@
* look in multiple places for assets. It can be either a directory (for
* finding assets as raw files on the disk) or a ZIP file. This newly
* added asset path will be examined first when searching for assets,
- * before any that were previously added.
+ * before any that were previously added, the assets are added as shared
+ * library if appAsLib is true.
*
* Returns "true" on success, "false" on failure. If 'cookie' is non-NULL,
* then on success, *cookie is set to the value corresponding to the
* newly-added asset source.
*/
- bool addAssetPath(const String8& path, int32_t* cookie);
+ bool addAssetPath(const String8& path, int32_t* cookie, bool appAsLib=false);
bool addOverlayPath(const String8& path, int32_t* cookie);
/*
@@ -280,7 +281,7 @@
const ResTable* getResTable(bool required = true) const;
void setLocaleLocked(const char* locale);
void updateResourceParamsLocked() const;
- bool appendPathToResTable(const asset_path& ap) const;
+ bool appendPathToResTable(const asset_path& ap, bool appAsLib=false) const;
Asset* openIdmapLocked(const struct asset_path& ap) const;
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index eff1f5f..49b6333 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1505,7 +1505,7 @@
class DynamicRefTable
{
public:
- DynamicRefTable(uint8_t packageId);
+ DynamicRefTable(uint8_t packageId, bool appAsLib);
// Loads an unmapped reference table from the package.
status_t load(const ResTable_lib_header* const header);
@@ -1530,6 +1530,7 @@
const uint8_t mAssignedPackageId;
uint8_t mLookupTable[256];
KeyedVector<String16, uint8_t> mEntries;
+ bool mAppAsLib;
};
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
@@ -1547,10 +1548,11 @@
status_t add(const void* data, size_t size, const int32_t cookie=-1, bool copyData=false);
status_t add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie=-1, bool copyData=false);
+ const int32_t cookie=-1, bool copyData=false, bool appAsLib=false);
status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false);
- status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false);
+ status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false,
+ bool appAsLib=false);
status_t add(ResTable* src);
status_t addEmpty(const int32_t cookie);
@@ -1858,7 +1860,7 @@
typedef Vector<Type*> TypeList;
status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData);
+ bool appAsLib, const int32_t cookie, bool copyData);
ssize_t getResourcePackageIndex(uint32_t resID) const;
@@ -1871,7 +1873,7 @@
size_t nameLen, uint32_t* outTypeSpecFlags) const;
status_t parsePackage(
- const ResTable_package* const pkg, const Header* const header);
+ const ResTable_package* const pkg, const Header* const header, bool appAsLib);
void print_value(const Package* pkg, const Res_value& value) const;
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/androidfw/Android.mk b/libs/androidfw/Android.mk
index 2f28700..f682fb8 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -40,10 +40,9 @@
# For the host
# =====================================================
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE:= libandroidfw
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
LOCAL_SRC_FILES:= $(hostSources)
@@ -56,13 +55,10 @@
# =====================================================
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE:= libandroidfw
-LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:= $(deviceSources)
LOCAL_C_INCLUDES := \
- external/zlib \
system/core/include
LOCAL_STATIC_LIBRARIES := libziparchive libbase
LOCAL_SHARED_LIBRARIES := \
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 623ea89..8a03b94 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -176,7 +176,7 @@
delete[] mVendor;
}
-bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
+bool AssetManager::addAssetPath(const String8& path, int32_t* cookie, bool appAsLib)
{
AutoMutex _l(mLock);
@@ -238,7 +238,7 @@
#endif
if (mResources != NULL) {
- appendPathToResTable(ap);
+ appendPathToResTable(ap, appAsLib);
}
return true;
@@ -610,7 +610,7 @@
return kFileTypeRegular;
}
-bool AssetManager::appendPathToResTable(const asset_path& ap) const {
+bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
// skip those ap's that correspond to system overlays
if (ap.isSystemOverlay) {
return true;
@@ -685,7 +685,7 @@
mResources->add(sharedRes);
} else {
ALOGV("Parsing resources for %s", ap.path.string());
- mResources->add(ass, idmap, nextEntryIdx + 1, !shared);
+ mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib);
}
onlyEmptyResources = false;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 37de89a..21b543e 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3080,13 +3080,13 @@
// table that defined the package); the ones after are skins on top of it.
struct ResTable::PackageGroup
{
- PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id)
+ PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id, bool appAsLib)
: owner(_owner)
, name(_name)
, id(_id)
, largestTypeId(0)
, bags(NULL)
- , dynamicRefTable(static_cast<uint8_t>(_id))
+ , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
{ }
~PackageGroup() {
@@ -3532,7 +3532,7 @@
{
memset(&mParams, 0, sizeof(mParams));
memset(mPackageMap, 0, sizeof(mPackageMap));
- addInternal(data, size, NULL, 0, cookie, copyData);
+ addInternal(data, size, NULL, 0, false, cookie, copyData);
LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
if (kDebugTableSuperNoisy) {
ALOGI("Creating ResTable %p\n", this);
@@ -3553,12 +3553,12 @@
}
status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
- return addInternal(data, size, NULL, 0, cookie, copyData);
+ return addInternal(data, size, NULL, 0, false, cookie, copyData);
}
status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData) {
- return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData);
+ const int32_t cookie, bool copyData, bool appAsLib) {
+ return addInternal(data, size, idmapData, idmapDataSize, appAsLib, cookie, copyData);
}
status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
@@ -3568,10 +3568,12 @@
return UNKNOWN_ERROR;
}
- return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData);
+ return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, false, 0, cookie,
+ copyData);
}
-status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) {
+status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData,
+ bool appAsLib) {
const void* data = asset->getBuffer(true);
if (data == NULL) {
ALOGW("Unable to get buffer of resource asset file");
@@ -3590,7 +3592,7 @@
}
return addInternal(data, static_cast<size_t>(asset->getLength()),
- idmapData, idmapSize, cookie, copyData);
+ idmapData, idmapSize, appAsLib, cookie, copyData);
}
status_t ResTable::add(ResTable* src)
@@ -3603,7 +3605,7 @@
for (size_t i=0; i<src->mPackageGroups.size(); i++) {
PackageGroup* srcPg = src->mPackageGroups[i];
- PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
+ PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id, false);
for (size_t j=0; j<srcPg->packages.size(); j++) {
pg->packages.add(srcPg->packages[j]);
}
@@ -3644,7 +3646,7 @@
}
status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData)
+ bool appAsLib, const int32_t cookie, bool copyData)
{
if (!data) {
return NO_ERROR;
@@ -3747,7 +3749,7 @@
return (mError=BAD_TYPE);
}
- if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {
+ if (parsePackage((ResTable_package*)chunk, header, appAsLib) != NO_ERROR) {
return mError;
}
curPackage++;
@@ -5935,7 +5937,7 @@
}
status_t ResTable::parsePackage(const ResTable_package* const pkg,
- const Header* const header)
+ const Header* const header, bool appAsLib)
{
const uint8_t* base = (const uint8_t*)pkg;
status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
@@ -5983,7 +5985,7 @@
if (id >= 256) {
LOG_ALWAYS_FATAL("Package id out of range");
return NO_ERROR;
- } else if (id == 0) {
+ } else if (id == 0 || appAsLib) {
// This is a library so assign an ID
id = mNextPackageId++;
}
@@ -6016,7 +6018,7 @@
char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
- group = new PackageGroup(this, String16(tmpName), id);
+ group = new PackageGroup(this, String16(tmpName), id, appAsLib);
if (group == NULL) {
delete package;
return (mError=NO_MEMORY);
@@ -6228,8 +6230,9 @@
return NO_ERROR;
}
-DynamicRefTable::DynamicRefTable(uint8_t packageId)
+DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
: mAssignedPackageId(packageId)
+ , mAppAsLib(appAsLib)
{
memset(mLookupTable, 0, sizeof(mLookupTable));
@@ -6314,16 +6317,18 @@
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
- if (packageId == APP_PACKAGE_ID) {
+ if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
// No lookup needs to be done, app package IDs are absolute.
return NO_ERROR;
}
- if (packageId == 0) {
+ if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) {
// The package ID is 0x00. That means that a shared library is accessing
- // its own local resource, so we fix up the resource with the calling
- // package ID.
- *resId |= ((uint32_t) mAssignedPackageId) << 24;
+ // its own local resource.
+ // Or if app resource is loaded as shared library, the resource which has
+ // app package Id is local resources.
+ // so we fix up those resources with the calling package ID.
+ *resId = (0xFFFFFF & (*resId)) | (((uint32_t) mAssignedPackageId) << 24);
return NO_ERROR;
}
@@ -6345,7 +6350,10 @@
}
status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
- if (value->dataType != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ if (value->dataType != Res_value::TYPE_DYNAMIC_REFERENCE &&
+ (value->dataType != Res_value::TYPE_REFERENCE || !mAppAsLib)) {
+ // If the package is loaded as shared library, the resource reference
+ // also need to be fixed.
return NO_ERROR;
}
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index a353575..2bc026b7 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -21,6 +21,7 @@
LOCAL_PATH:= $(call my-dir)
testFiles := \
+ AppAsLib_test.cpp \
AttributeFinder_test.cpp \
ByteBucketArray_test.cpp \
Config_test.cpp \
diff --git a/libs/androidfw/tests/AppAsLib_test.cpp b/libs/androidfw/tests/AppAsLib_test.cpp
new file mode 100644
index 0000000..bdb0c3d
--- /dev/null
+++ b/libs/androidfw/tests/AppAsLib_test.cpp
@@ -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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include "data/basic/R.h"
+#include "data/appaslib/R.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+#include "data/basic/basic_arsc.h"
+
+TEST(AppAsLibTest, loadedAsApp) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(base::R::integer::number2, &val);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(base::R::array::integerArray1, val.data);
+}
+
+TEST(AppAsLibTest, loadedAsSharedLib) {
+ ResTable table;
+ // Load as shared library.
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len, NULL, 0, -1, false, true));
+
+ Res_value val;
+ ssize_t block = table.getResource(appaslib::R::integer::number2, &val);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(appaslib::R::array::integerArray1, val.data);
+}
+
+}
diff --git a/libs/androidfw/tests/data/appaslib/R.h b/libs/androidfw/tests/data/appaslib/R.h
new file mode 100644
index 0000000..f89d4bf
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/R.h
@@ -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.
+ */
+
+#ifndef __APPASLIB_R_H
+#define __APPASLIB_R_H
+
+namespace appaslib {
+namespace R {
+
+namespace integer {
+ enum {
+ number2 = 0x02040001, // default
+ };
+}
+
+namespace array {
+ enum {
+ integerArray1 = 0x02060000, // default
+ };
+}
+
+} // namespace R
+} // namespace appaslib
+
+#endif // __APPASLIB_R_H
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 676ab1c..3eb13ab 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -165,6 +165,7 @@
LOCAL_CFLAGS := $(hwui_cflags)
LOCAL_SRC_FILES += \
+ unit_tests/CanvasStateTests.cpp \
unit_tests/ClipAreaTests.cpp \
unit_tests/DamageAccumulatorTests.cpp \
unit_tests/LinearAllocatorTests.cpp
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index e307ad9..c128ca7 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -34,12 +34,17 @@
}
-CanvasState::~CanvasState() {
-
-}
-
-void CanvasState::initializeSaveStack(float clipLeft, float clipTop,
+void CanvasState::initializeSaveStack(
+ int viewportWidth, int viewportHeight,
+ float clipLeft, float clipTop,
float clipRight, float clipBottom, const Vector3& lightCenter) {
+ if (mWidth != viewportWidth || mHeight != viewportHeight) {
+ mWidth = viewportWidth;
+ mHeight = viewportHeight;
+ mFirstSnapshot->initializeViewport(viewportWidth, viewportHeight);
+ mCanvas.onViewportInitialized();
+ }
+
mSnapshot = new Snapshot(mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
@@ -48,20 +53,6 @@
mSaveCount = 1;
}
-void CanvasState::setViewport(int width, int height) {
- mWidth = width;
- mHeight = height;
- mFirstSnapshot->initializeViewport(width, height);
- mCanvas.onViewportInitialized();
-
- // create a temporary 1st snapshot, so old snapshots are released,
- // and viewport can be queried safely.
- // TODO: remove, combine viewport + save stack initialization
- mSnapshot = new Snapshot(mFirstSnapshot,
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- mSaveCount = 1;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Save (layer)
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index b35db28..f0fb9ba 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -71,20 +71,18 @@
* (getClip/Matrix), but so that quickRejection can also be used.
*/
-class ANDROID_API CanvasState {
+class CanvasState {
public:
CanvasState(CanvasStateClient& renderer);
- ~CanvasState();
/**
* Initializes the first snapshot, computing the projection matrix,
* and stores the dimensions of the render target.
*/
- void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom,
+ void initializeSaveStack(int viewportWidth, int viewportHeight,
+ float clipLeft, float clipTop, float clipRight, float clipBottom,
const Vector3& lightCenter);
- void setViewport(int width, int height);
-
bool hasRectToRectTransform() const {
return CC_LIKELY(currentTransform()->rectToRect());
}
@@ -159,16 +157,11 @@
int getHeight() const { return mHeight; }
bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
- inline const Snapshot* currentSnapshot() const {
- return mSnapshot != nullptr ? mSnapshot.get() : mFirstSnapshot.get();
- }
+ inline const Snapshot* currentSnapshot() const { return mSnapshot.get(); }
inline Snapshot* writableSnapshot() { return mSnapshot.get(); }
inline const Snapshot* firstSnapshot() const { return mFirstSnapshot.get(); }
private:
- /// No default constructor - must supply a CanvasStateClient (mCanvas).
- CanvasState();
-
/// indicates that the clip has been changed since the last time it was consumed
bool mDirtyClip;
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 506bfad..77bde86 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -54,8 +54,8 @@
"prepareDirty called a second time during a recording!");
mDisplayListData = new DisplayListData();
- mState.setViewport(width, height);
- mState.initializeSaveStack(0, 0, mState.getWidth(), mState.getHeight(), Vector3());
+ mState.initializeSaveStack(width, height,
+ 0, 0, width, height, Vector3());
mDeferredBarrierType = kBarrier_InOrder;
mState.setDirtyClip(false);
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index e748221..8d85289 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -236,8 +236,7 @@
DeferStateStruct deferredState(*deferredList, *renderer,
RenderNode::kReplayFlag_ClipChildren);
- renderer->setViewport(width, height);
- renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
+ renderer->setupFrameState(width, height, dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, !isBlend());
renderNode->computeOrdering();
@@ -258,9 +257,8 @@
ATRACE_LAYER_WORK("Issue");
renderer->startMark((renderNode.get() != nullptr) ? renderNode->getName() : "Layer");
- renderer->setViewport(layer.getWidth(), layer.getHeight());
- renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
- !isBlend());
+ renderer->prepareDirty(layer.getWidth(), layer.getHeight(),
+ dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend());
deferredList->flush(*renderer, dirtyRect);
@@ -277,9 +275,8 @@
ATRACE_LAYER_WORK("Direct-Issue");
updateLightPosFromRenderer(rootRenderer);
- renderer->setViewport(layer.getWidth(), layer.getHeight());
- renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
- !isBlend());
+ renderer->prepareDirty(layer.getWidth(), layer.getHeight(),
+ dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend());
renderer->drawRenderNode(renderNode.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren);
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index d8e6392..c63b559 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -43,8 +43,8 @@
LayerRenderer::~LayerRenderer() {
}
-void LayerRenderer::prepareDirty(float left, float top, float right, float bottom,
- bool opaque) {
+void LayerRenderer::prepareDirty(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque) {
LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
mRenderState.bindFramebuffer(mLayer->getFbo());
@@ -64,7 +64,8 @@
}
mLayer->clipRect.set(dirty);
- OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
+ OpenGLRenderer::prepareDirty(viewportWidth, viewportHeight,
+ dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
}
void LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
@@ -430,9 +431,8 @@
{
LayerRenderer renderer(renderState, layer);
- renderer.setViewport(bitmap->width(), bitmap->height());
- renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
- bitmap->width(), bitmap->height(), !layer->isBlend());
+ renderer.OpenGLRenderer::prepareDirty(bitmap->width(), bitmap->height(),
+ 0.0f, 0.0f, bitmap->width(), bitmap->height(), !layer->isBlend());
renderState.scissor().setEnabled(false);
renderer.translate(0.0f, bitmap->height());
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 47ded7e..e4a54b0 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -50,8 +50,8 @@
virtual ~LayerRenderer();
virtual void onViewportInitialized() override { /* do nothing */ }
- virtual void prepareDirty(float left, float top, float right, float bottom,
- bool opaque) override;
+ virtual void prepareDirty(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque) override;
virtual void clear(float left, float top, float right, float bottom, bool opaque) override;
virtual bool finish() override;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5692d7e..e06e348 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -113,10 +113,11 @@
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
-void OpenGLRenderer::setupFrameState(float left, float top,
- float right, float bottom, bool opaque) {
+void OpenGLRenderer::setupFrameState(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque) {
mCaches.clearGarbage();
- mState.initializeSaveStack(left, top, right, bottom, mLightCenter);
+ mState.initializeSaveStack(viewportWidth, viewportHeight,
+ left, top, right, bottom, mLightCenter);
mOpaque = opaque;
mTilingClip.set(left, top, right, bottom);
}
@@ -137,10 +138,10 @@
mTilingClip.right, mTilingClip.bottom, mOpaque);
}
-void OpenGLRenderer::prepareDirty(float left, float top,
- float right, float bottom, bool opaque) {
+void OpenGLRenderer::prepareDirty(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque) {
- setupFrameState(left, top, right, bottom, opaque);
+ setupFrameState(viewportWidth, viewportHeight, left, top, right, bottom, opaque);
// Layer renderers will start the frame immediately
// The framebuffer renderer will first defer the display list
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index af85e8c..910af57 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -119,15 +119,6 @@
OpenGLRenderer(RenderState& renderState);
virtual ~OpenGLRenderer();
- /**
- * Sets the dimension of the underlying drawing surface. This method must
- * be called at least once every time the drawing surface changes size.
- *
- * @param width The width in pixels of the underlysing surface
- * @param height The height in pixels of the underlysing surface
- */
- void setViewport(int width, int height) { mState.setViewport(width, height); }
-
void initProperties();
void initLight(float lightRadius, uint8_t ambientShadowAlpha,
uint8_t spotShadowAlpha);
@@ -143,21 +134,8 @@
* and will not be cleared. If false, the target surface
* will be cleared
*/
- virtual void prepareDirty(float left, float top, float right, float bottom,
- bool opaque);
-
- /**
- * Prepares the renderer to draw a frame. This method must be invoked
- * at the beginning of each frame. When this method is invoked, the
- * entire drawing surface is assumed to be redrawn.
- *
- * @param opaque If true, the target surface is considered opaque
- * and will not be cleared. If false, the target surface
- * will be cleared
- */
- void prepare(bool opaque) {
- prepareDirty(0.0f, 0.0f, mState.getWidth(), mState.getHeight(), opaque);
- }
+ virtual void prepareDirty(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque);
/**
* Indicates the end of a frame. This method must be invoked whenever
@@ -430,7 +408,8 @@
* Perform the setup specific to a frame. This method does not
* issue any OpenGL commands.
*/
- void setupFrameState(float left, float top, float right, float bottom, bool opaque);
+ void setupFrameState(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque);
/**
* Indicates the start of rendering. This method will setup the
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/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 1673802..b74b508 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -248,7 +248,7 @@
Frame frame = mEglManager.beginFrame(mEglSurface);
if (frame.width() != mCanvas->getViewportWidth()
|| frame.height() != mCanvas->getViewportHeight()) {
- mCanvas->setViewport(frame.width(), frame.height());
+ // can't rely on prior content of window if viewport size changes
dirty.setEmpty();
} else if (mHaveNewSurface || frame.bufferAge() == 0) {
// New surface needs a full draw
@@ -295,8 +295,8 @@
mDamageHistory.next() = screenDirty;
mEglManager.damageFrame(frame, dirty);
- mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
- dirty.fRight, dirty.fBottom, mOpaque);
+ mCanvas->prepareDirty(frame.width(), frame.height(),
+ dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque);
Rect outBounds;
mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
diff --git a/libs/hwui/unit_tests/CanvasStateTests.cpp b/libs/hwui/unit_tests/CanvasStateTests.cpp
new file mode 100644
index 0000000..dfbf6d3
--- /dev/null
+++ b/libs/hwui/unit_tests/CanvasStateTests.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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 "CanvasState.h"
+
+#include "Matrix.h"
+#include "Rect.h"
+#include "utils/LinearAllocator.h"
+
+#include <gtest/gtest.h>
+#include <SkPath.h>
+#include <SkRegion.h>
+#include <SkCanvas.h>
+
+namespace android {
+namespace uirenderer {
+
+class NullClient: public CanvasStateClient {
+ void onViewportInitialized() override {}
+ void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+ GLuint getTargetFbo() const override { return 0; }
+};
+
+static NullClient sNullClient;
+
+static bool approxEqual(const Matrix4& a, const Matrix4& b) {
+ for (int i = 0; i < 16; i++) {
+ if (!MathUtils::areEqual(a[i], b[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+TEST(CanvasState, gettersAndSetters) {
+ CanvasState state(sNullClient);
+ state.initializeSaveStack(200, 200,
+ 0, 0, 200, 200, Vector3());
+
+ ASSERT_EQ(state.getWidth(), 200);
+ ASSERT_EQ(state.getHeight(), 200);
+
+ Matrix4 simpleTranslate;
+ simpleTranslate.loadTranslate(10, 20, 0);
+ state.setMatrix(simpleTranslate);
+
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 200, 200));
+ ASSERT_EQ(state.getLocalClipBounds(), Rect(-10, -20, 190, 180));
+ EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
+ EXPECT_TRUE(state.clipIsSimple());
+}
+
+TEST(CanvasState, simpleClipping) {
+ CanvasState state(sNullClient);
+ state.initializeSaveStack(200, 200,
+ 0, 0, 200, 200, Vector3());
+
+ state.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 100, 100));
+
+ state.clipRect(10, 10, 200, 200, SkRegion::kIntersect_Op);
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
+
+ state.clipRect(50, 50, 150, 150, SkRegion::kReplace_Op);
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150));
+}
+
+TEST(CanvasState, complexClipping) {
+ CanvasState state(sNullClient);
+ state.initializeSaveStack(200, 200,
+ 0, 0, 200, 200, Vector3());
+
+ state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ {
+ // rotated clip causes complex clip
+ state.rotate(10);
+ EXPECT_TRUE(state.clipIsSimple());
+ state.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+ EXPECT_FALSE(state.clipIsSimple());
+ }
+ state.restore();
+
+ state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ {
+ // subtracted clip causes complex clip
+ EXPECT_TRUE(state.clipIsSimple());
+ state.clipRect(50, 50, 150, 150, SkRegion::kDifference_Op);
+ EXPECT_FALSE(state.clipIsSimple());
+ }
+ state.restore();
+
+ state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ {
+ // complex path causes complex clip
+ SkPath path;
+ path.addOval(SkRect::MakeWH(200, 200));
+ EXPECT_TRUE(state.clipIsSimple());
+ state.clipPath(&path, SkRegion::kDifference_Op);
+ EXPECT_FALSE(state.clipIsSimple());
+ }
+ state.restore();
+}
+
+TEST(CanvasState, saveAndRestore) {
+ CanvasState state(sNullClient);
+ state.initializeSaveStack(200, 200,
+ 0, 0, 200, 200, Vector3());
+
+ state.save(SkCanvas::kClip_SaveFlag);
+ {
+ state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 10, 10));
+ }
+ state.restore();
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 200, 200)); // verify restore
+
+ Matrix4 simpleTranslate;
+ simpleTranslate.loadTranslate(10, 10, 0);
+ state.save(SkCanvas::kMatrix_SaveFlag);
+ {
+ state.translate(10, 10, 0);
+ EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
+ }
+ state.restore();
+ EXPECT_FALSE(approxEqual(*state.currentTransform(), simpleTranslate));
+}
+
+TEST(CanvasState, saveAndRestoreButNotTooMuch) {
+ CanvasState state(sNullClient);
+ state.initializeSaveStack(200, 200,
+ 0, 0, 200, 200, Vector3());
+
+ state.save(SkCanvas::kMatrix_SaveFlag); // NOTE: clip not saved
+ {
+ state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 10, 10));
+ }
+ state.restore();
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 10, 10)); // verify not restored
+
+ Matrix4 simpleTranslate;
+ simpleTranslate.loadTranslate(10, 10, 0);
+ state.save(SkCanvas::kClip_SaveFlag); // NOTE: matrix not saved
+ {
+ state.translate(10, 10, 0);
+ EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
+ }
+ state.restore();
+ EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); // verify not restored
+}
+
+}
+}
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index 01cf5d2..0abe88b 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -140,7 +140,7 @@
}
void* LinearAllocator::start(Page* p) {
- return ALIGN_PTR(((size_t*)p) + sizeof(Page));
+ return ALIGN_PTR((size_t)p + sizeof(Page));
}
void* LinearAllocator::end(Page* p) {
diff --git a/tools/aidl/os.h b/media/java/android/media/midi/IBluetoothMidiService.aidl
similarity index 61%
rename from tools/aidl/os.h
rename to media/java/android/media/midi/IBluetoothMidiService.aidl
index 79d2c35..fe5566d 100644
--- a/tools/aidl/os.h
+++ b/media/java/android/media/midi/IBluetoothMidiService.aidl
@@ -1,11 +1,11 @@
/*
- * 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,
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#ifndef _FRAMEWORKS_BASE_TOOLS_AIDL_OS_SEP_H_
-#define _FRAMEWORKS_BASE_TOOLS_AIDL_OS_SEP_H_
+package android.media.midi;
-#if defined(_WIN32)
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
+import android.bluetooth.BluetoothDevice;
+import android.os.IBinder;
-#endif
+/** @hide */
+interface IBluetoothMidiService
+{
+ IBinder addBluetoothDevice(in BluetoothDevice bluetoothDevice);
+}
diff --git a/media/java/android/mtp/MtpObjectInfo.java b/media/java/android/mtp/MtpObjectInfo.java
index a080c73..64aa997 100644
--- a/media/java/android/mtp/MtpObjectInfo.java
+++ b/media/java/android/mtp/MtpObjectInfo.java
@@ -294,6 +294,11 @@
mObjectInfo.mThumbPixWidth = objectInfo.mThumbPixWidth;
}
+ public Builder setObjectHandle(int value) {
+ mObjectInfo.mHandle = value;
+ return this;
+ }
+
public Builder setAssociationDesc(int value) {
mObjectInfo.mAssociationDesc = value;
return this;
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 218a117..93a4426 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -440,6 +440,12 @@
// if this is an ABuffer that doesn't actually hold any accessible memory,
// use a null ByteBuffer
*buf = NULL;
+
+ if (buffer == NULL) {
+ ALOGV("createByteBufferFromABuffer - given NULL, returning NULL");
+ return OK;
+ }
+
if (buffer->base() == NULL) {
return OK;
}
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/Android.mk b/media/packages/BluetoothMidiService/Android.mk
index 2c9c3c5..0565925 100644
--- a/media/packages/BluetoothMidiService/Android.mk
+++ b/media/packages/BluetoothMidiService/Android.mk
@@ -3,7 +3,8 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES += \
+ $(call all-java-files-under,src)
LOCAL_PACKAGE_NAME := BluetoothMidiService
LOCAL_CERTIFICATE := platform
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index b0b389a..1cfd55d 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -8,7 +8,7 @@
<application
android:label="@string/app_name">
- <service android:name="BluetoothMidiService"
+ <service android:name=".BluetoothMidiService"
android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
<intent-filter>
<action android:name="android.media.midi.BluetoothMidiService" />
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java
index 1f81a05..5541f9f 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java
@@ -19,6 +19,7 @@
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
+import android.media.midi.IBluetoothMidiService;
import android.media.midi.MidiManager;
import android.os.IBinder;
import android.util.Log;
@@ -34,25 +35,31 @@
@Override
public IBinder onBind(Intent intent) {
- if (MidiManager.BLUETOOTH_MIDI_SERVICE_INTENT.equals(intent.getAction())) {
- BluetoothDevice bluetoothDevice = (BluetoothDevice)intent.getParcelableExtra("device");
+ // Return the interface
+ return mBinder;
+ }
+
+
+ private final IBluetoothMidiService.Stub mBinder = new IBluetoothMidiService.Stub() {
+
+ public IBinder addBluetoothDevice(BluetoothDevice bluetoothDevice) {
+ BluetoothMidiDevice device;
if (bluetoothDevice == null) {
- Log.e(TAG, "no BluetoothDevice in onBind intent");
+ Log.e(TAG, "no BluetoothDevice in addBluetoothDevice()");
return null;
}
-
- BluetoothMidiDevice device;
synchronized (mDeviceServerMap) {
device = mDeviceServerMap.get(bluetoothDevice);
if (device == null) {
- device = new BluetoothMidiDevice(this, bluetoothDevice, this);
+ device = new BluetoothMidiDevice(BluetoothMidiService.this,
+ bluetoothDevice, BluetoothMidiService.this);
mDeviceServerMap.put(bluetoothDevice, device);
}
}
return device.getBinder();
}
- return null;
- }
+
+ };
void deviceClosed(BluetoothDevice device) {
synchronized (mDeviceServerMap) {
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/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 3430bb47..1a4e3eb 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -5,9 +5,29 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 \
- android-support-v7-recyclerview \
- guava
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+# The design lib requires that the client package use appcompat themes.
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
+# Supplies material design components, e.g. Snackbar.
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-design
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
+LOCAL_STATIC_JAVA_LIBRARIES += guava
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+# Not quite sure why it is necessary to explicitly pull in resources from the
+# appcompat lib, but the demo code indicates it's necessary (see
+# development/samples/Support7Demos/Android.mk)
+LOCAL_RESOURCE_DIR += \
+ frameworks/support/v7/appcompat/res \
+ frameworks/support/design/res \
+ frameworks/support/v7/recyclerview/res
+
+# Again, required to pull in appcompat resources. See abovementioned demo code.
+LOCAL_AAPT_FLAGS := \
+ --auto-add-overlay \
+ --extra-packages android.support.v7.appcompat \
+ --extra-packages android.support.design \
+ --extra-packages android.support.v7.recyclerview
LOCAL_PACKAGE_NAME := DocumentsUI
LOCAL_CERTIFICATE := platform
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index d578769..97bc8fd 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -36,12 +36,18 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
- <action android:name="android.provider.action.MANAGE_ROOT" />
+ <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>
+
+ <activity
+ android:name=".ManageRootActivity"
+ android:theme="@style/DocumentsNonDialogTheme"
+ android:icon="@drawable/ic_doc_text">
<intent-filter>
- <action android:name="android.provider.action.BROWSE_DOCUMENT_ROOT" />
+ <action android:name="android.provider.action.MANAGE_ROOT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.document/root" />
</intent-filter>
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/layout/directory_cluster.xml b/packages/DocumentsUI/res/layout/directory_cluster.xml
new file mode 100644
index 0000000..e47e196
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/directory_cluster.xml
@@ -0,0 +1,34 @@
+<?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.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 221de13..eba9af4 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -25,7 +25,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"
@@ -50,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/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..7df152f 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -26,7 +26,8 @@
android:id="@+id/menu_create_dir"
android:title="@string/menu_create_dir"
android:icon="@drawable/ic_menu_new_folder"
- android:showAsAction="always" />
+ android:showAsAction="always"
+ android:visible="false" />
<item
android:id="@+id/menu_sort"
android:title="@string/menu_sort"
@@ -62,10 +63,12 @@
android:visible="false" />
<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 6aa41e0..0053f22 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Skuif tans <xliff:g id="COUNT_1">%1$d</xliff:g> lêers.</item>
<item quantity="one">Skuif tans <xliff:g id="COUNT_0">%1$d</xliff:g> lêer.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Vee tans <xliff:g id="COUNT_1">%1$d</xliff:g> lêers uit.</item>
+ <item quantity="one">Vee tans <xliff:g id="COUNT_0">%1$d</xliff:g> lêer uit.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Ontdoen"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Maak tans gereed vir kopieer …"</string>
<string name="move_preparing" msgid="2772219441375531410">"Berei tans voor vir skuif …"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index 9a2051e..eb88830 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎችን በመውሰድ ላይ።</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎችን በመውሰድ ላይ።</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎችን በመሰረዝ ላይ።</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎችን በመሰረዝ ላይ።</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"ቀልብስ"</string>
<string name="copy_preparing" msgid="3896202461003039386">"ቅጂ በማዘጋጀት ላይ…"</string>
<string name="move_preparing" msgid="2772219441375531410">"ለመውሰድ በማዘጋጀት ላይ…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index 18d8f41..bf464b6 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -81,6 +81,15 @@
<item quantity="other">جارٍ نقل <xliff:g id="COUNT_1">%1$d</xliff:g> من الملفات.</item>
<item quantity="one">جارٍ نقل <xliff:g id="COUNT_0">%1$d</xliff:g> ملف واحد.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="zero">جارٍ حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملف.</item>
+ <item quantity="two">جارٍ حذف ملفين (<xliff:g id="COUNT_1">%1$d</xliff:g>).</item>
+ <item quantity="few">جارٍ حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملفات.</item>
+ <item quantity="many">جارٍ حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملفًا.</item>
+ <item quantity="other">جارٍ حذف <xliff:g id="COUNT_1">%1$d</xliff:g> من الملفات.</item>
+ <item quantity="one">جارٍ حذف <xliff:g id="COUNT_0">%1$d</xliff:g> ملف.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"تراجع"</string>
<string name="copy_preparing" msgid="3896202461003039386">"جارٍ التحضير للنسخ ..."</string>
<string name="move_preparing" msgid="2772219441375531410">"جارٍ التحضير للنقل…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index 8c6c502..0bd01a5 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> fayl köçürülür.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> fayl köçürülür.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> fayl silinir.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> fayl silinir.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Ləğv edin"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Kopyalanmaq üçün hazırlanır ..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Köçürmə üçün hazırlanır..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index bd14ea5..669145e 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> файла се преместват.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> файл се премества.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Изтриват се <xliff:g id="COUNT_1">%1$d</xliff:g> файла.</item>
+ <item quantity="one">Изтрива се <xliff:g id="COUNT_0">%1$d</xliff:g> файл.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Отмяна"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Подготвя се за копиране…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Преместването се подготвя…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index ded6042..240ba6c 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল সরানো হচ্ছে৷</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল সরানো হচ্ছে৷</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল মোছা হচ্ছে।</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল মোছা হচ্ছে।</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"পূর্বাবস্থায় ফিরুন"</string>
<string name="copy_preparing" msgid="3896202461003039386">"অনুলিপি করার জন্য প্রস্তুত করা হচ্ছে..."</string>
<string name="move_preparing" msgid="2772219441375531410">"সরানোর জন্য প্রস্তুত হচ্ছে..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index 336e71b..30accda 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">S\'estan movent <xliff:g id="COUNT_1">%1$d</xliff:g> fitxers.</item>
<item quantity="one">S\'està movent <xliff:g id="COUNT_0">%1$d</xliff:g> fitxer.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">S\'estan suprimint <xliff:g id="COUNT_1">%1$d</xliff:g> fitxers.</item>
+ <item quantity="one">S\'està suprimint <xliff:g id="COUNT_0">%1$d</xliff:g> fitxer.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Desfés"</string>
<string name="copy_preparing" msgid="3896202461003039386">"S\'està preparant una còpia…"</string>
<string name="move_preparing" msgid="2772219441375531410">"S\'està preparant per moure\'ls..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index 6f969ba..cb1d973 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -77,6 +77,13 @@
<item quantity="other">Přesouvá se <xliff:g id="COUNT_1">%1$d</xliff:g> souborů.</item>
<item quantity="one">Přesouvá se <xliff:g id="COUNT_0">%1$d</xliff:g> soubor.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="few">Mazání <xliff:g id="COUNT_1">%1$d</xliff:g> souborů.</item>
+ <item quantity="many">Mazání <xliff:g id="COUNT_1">%1$d</xliff:g> souboru.</item>
+ <item quantity="other">Mazání <xliff:g id="COUNT_1">%1$d</xliff:g> souborů.</item>
+ <item quantity="one">Mazání <xliff:g id="COUNT_0">%1$d</xliff:g> souboru.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Vrátit zpět"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Příprava na kopírování…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Příprava na přesunutí…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index a2b570e..f12737c 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">Flytter <xliff:g id="COUNT_1">%1$d</xliff:g> filer.</item>
<item quantity="other">Flytter <xliff:g id="COUNT_1">%1$d</xliff:g> filer.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> filer slettes.</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> filer slettes.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Fortryd"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Forbereder kopiering…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Forbereder flytning…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 1f194c5..a7be4db 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> Dateien werden verschoben.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> Datei wird verschoben.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> Dateien werden gelöscht.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> Datei wird gelöscht.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Rückgängig machen"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Kopieren wird vorbereitet…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Verschieben wird vorbereitet…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index c95a8a5..82155a8 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Μετακίνηση <xliff:g id="COUNT_1">%1$d</xliff:g> αρχείων.</item>
<item quantity="one">Μετακίνηση <xliff:g id="COUNT_0">%1$d</xliff:g> αρχείου.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Διαγραφή <xliff:g id="COUNT_1">%1$d</xliff:g> αρχείων.</item>
+ <item quantity="one">Διαγραφή <xliff:g id="COUNT_0">%1$d</xliff:g> αρχείου.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Αναίρεση"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Προετοιμασία για αντιγραφή…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Προετοιμασία για μετακίνηση…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index b56642f..c609047 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Moving <xliff:g id="COUNT_1">%1$d</xliff:g> files.</item>
<item quantity="one">Moving <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Deleting <xliff:g id="COUNT_1">%1$d</xliff:g> files.</item>
+ <item quantity="one">Deleting <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Undo"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Preparing for copy…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Preparing for move…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index b56642f..c609047 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Moving <xliff:g id="COUNT_1">%1$d</xliff:g> files.</item>
<item quantity="one">Moving <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Deleting <xliff:g id="COUNT_1">%1$d</xliff:g> files.</item>
+ <item quantity="one">Deleting <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Undo"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Preparing for copy…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Preparing for move…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index b56642f..c609047 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Moving <xliff:g id="COUNT_1">%1$d</xliff:g> files.</item>
<item quantity="one">Moving <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Deleting <xliff:g id="COUNT_1">%1$d</xliff:g> files.</item>
+ <item quantity="one">Deleting <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Undo"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Preparing for copy…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Preparing for move…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 4655d67..5936d83 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Moviendo <xliff:g id="COUNT_1">%1$d</xliff:g> archivos</item>
<item quantity="one">Moviendo <xliff:g id="COUNT_0">%1$d</xliff:g> archivo</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Se están borrando <xliff:g id="COUNT_1">%1$d</xliff:g> archivos.</item>
+ <item quantity="one">Se está borrando <xliff:g id="COUNT_0">%1$d</xliff:g> archivo.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Deshacer"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Preparando para copiar…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Preparación para mover archivos…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 1f985ea..2ed67dd 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Moviendo <xliff:g id="COUNT_1">%1$d</xliff:g> archivos.</item>
<item quantity="one">Moviendo <xliff:g id="COUNT_0">%1$d</xliff:g> archivo.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Eliminando <xliff:g id="COUNT_1">%1$d</xliff:g> archivos.</item>
+ <item quantity="one">Eliminando <xliff:g id="COUNT_0">%1$d</xliff:g> archivo.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Deshacer"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Preparando para copiar..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Preparando para mover…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index de92348..e32936f 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Teisaldatakse <xliff:g id="COUNT_1">%1$d</xliff:g> faili.</item>
<item quantity="one">Teisaldatakse <xliff:g id="COUNT_0">%1$d</xliff:g> fail.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Kustutatakse <xliff:g id="COUNT_1">%1$d</xliff:g> faili.</item>
+ <item quantity="one">Kustutatakse <xliff:g id="COUNT_0">%1$d</xliff:g> fail.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Võta tagasi"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Kopeerimise ettevalmistamine …"</string>
<string name="move_preparing" msgid="2772219441375531410">"Teisaldamise ettevalmistamine …"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index 5808045..a1b6fc2 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> fitxategi mugitzen.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> fitxategi mugitzen.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> fitxategi ezabatzen ari dira.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> fitxategi ezabatzen ari da.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Desegin"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Kopiatzeko prestatzen…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Mugitzeko prestatzen…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index a857ee7..bbbfe52 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">در حال انتقال <xliff:g id="COUNT_1">%1$d</xliff:g> فایل.</item>
<item quantity="other">در حال انتقال <xliff:g id="COUNT_1">%1$d</xliff:g> فایل.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">در حال حذف <xliff:g id="COUNT_1">%1$d</xliff:g> فایل.</item>
+ <item quantity="other">در حال حذف <xliff:g id="COUNT_1">%1$d</xliff:g> فایل.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"لغو"</string>
<string name="copy_preparing" msgid="3896202461003039386">"در حال آمادهسازی برای کپی..."</string>
<string name="move_preparing" msgid="2772219441375531410">"درحال آمادهسازی برای انتقال…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index df21358a..fd289a7 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Siirretään <xliff:g id="COUNT_1">%1$d</xliff:g> tiedostoa.</item>
<item quantity="one">Siirretään <xliff:g id="COUNT_0">%1$d</xliff:g> tiedosto.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Poistetaan <xliff:g id="COUNT_1">%1$d</xliff:g> tiedostoa.</item>
+ <item quantity="one">Poistetaan <xliff:g id="COUNT_0">%1$d</xliff:g> tiedosto.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Kumoa"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Valmistellaan kopiointia…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Valmistellaan siirtämistä…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index f86175a..342fc97 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">Déplacement de <xliff:g id="COUNT_1">%1$d</xliff:g> fichier en cours.</item>
<item quantity="other">Déplacement de <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers en cours.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Suppression de <xliff:g id="COUNT_1">%1$d</xliff:g> fichier.</item>
+ <item quantity="other">Suppression de <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Annuler"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Préparation de la copie en cours"</string>
<string name="move_preparing" msgid="2772219441375531410">"Préparation du déplacement..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index de4a6fc..7434e2f 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">Déplacement de <xliff:g id="COUNT_1">%1$d</xliff:g> fichier en cours…</item>
<item quantity="other">Déplacement de <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers en cours…</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Suppression de <xliff:g id="COUNT_1">%1$d</xliff:g> fichier en cours…</item>
+ <item quantity="other">Suppression de <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers en cours…</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Annuler"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Préparation de la copie en cours…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Préparation au déplacement…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index c5f46f9..b74018d 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Movendo <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros.</item>
<item quantity="one">Movendo <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Eliminando <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros.</item>
+ <item quantity="one">Eliminando <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Desfacer"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Preparando para copiar…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Preparándose para mover..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 9c48562..58384c9 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલો ખસેડી રહ્યાં છે.</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલો ખસેડી રહ્યાં છે.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલ કાઢી નાખી રહ્યાં છે.</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલ કાઢી નાખી રહ્યાં છે.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"પૂર્વવત્ કરો"</string>
<string name="copy_preparing" msgid="3896202461003039386">"કૉપિ માટે તૈયારી કરી રહ્યું છે…"</string>
<string name="move_preparing" msgid="2772219441375531410">"ખસેડવા માટે તૈયાર કરી રહ્યું છે…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index 38d2f8e..b42c69c 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one"> <xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें ले जाई जा रही हैं.</item>
<item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें ले जाई जा रही हैं.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें हटाई जा रही हैं.</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें हटाई जा रही हैं.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"वापस लाएं"</string>
<string name="copy_preparing" msgid="3896202461003039386">"कॉपी करने की तैयारी हो रही है…"</string>
<string name="move_preparing" msgid="2772219441375531410">"ले जाने की तैयारी हो रही है…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 90c050b..575c7ff 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -75,6 +75,12 @@
<item quantity="few">Premještanje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke.</item>
<item quantity="other">Premještanje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Brisanje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke.</item>
+ <item quantity="few">Brisanje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke.</item>
+ <item quantity="other">Brisanje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Poništi"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Priprema za kopiranje…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Priprema za premještanje…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index 0b9d562..2d2e3c8 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> fájl áthelyezése.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> fájl áthelyezése.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> fájl törlése.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> fájl törlése.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Visszavonás"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Felkészülés a másolásra…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Áthelyezés előkészítése…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 6b0816d..0a4e7a3 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ֆայլի տեղափոխում:</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ֆայլի տեղափոխում:</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ֆայլի ջնջում:</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ֆայլի ջնջում:</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Հետարկել"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Պատճենման նախապատրաստում…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Տեղափոխման նախապատրաստում…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 581d8ab..59aadf1 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Memindahkan <xliff:g id="COUNT_1">%1$d</xliff:g> file.</item>
<item quantity="one">Memindahkan <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Menghapus <xliff:g id="COUNT_1">%1$d</xliff:g> file.</item>
+ <item quantity="one">Menghapus <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Urungkan"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Menyiapkan salinan..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Menyiapkan pemindahan…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index 668d2f9..a0f4987 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">Færir <xliff:g id="COUNT_1">%1$d</xliff:g> skrá.</item>
<item quantity="other">Færir <xliff:g id="COUNT_1">%1$d</xliff:g> skrár.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Eyðir <xliff:g id="COUNT_1">%1$d</xliff:g> skrá.</item>
+ <item quantity="other">Eyðir <xliff:g id="COUNT_1">%1$d</xliff:g> skrám.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Afturkalla"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Undirbúningur fyrir afritun…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Flutningur undirbúinn…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index fe16e63..ce837da 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Spostamento di <xliff:g id="COUNT_1">%1$d</xliff:g> file.</item>
<item quantity="one">Spostamento di <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Eliminazione di <xliff:g id="COUNT_1">%1$d</xliff:g> file.</item>
+ <item quantity="one">Eliminazione di <xliff:g id="COUNT_0">%1$d</xliff:g> file.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Annulla"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Preparazione alla copia…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Preparazione dello spostamento…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index 032c545..89e6403 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -77,6 +77,13 @@
<item quantity="other">מעביר <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים.</item>
<item quantity="one">מעביר קובץ <xliff:g id="COUNT_0">%1$d</xliff:g>.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="two">מוחק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים.</item>
+ <item quantity="many">מוחק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים.</item>
+ <item quantity="other">מוחק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים.</item>
+ <item quantity="one"> מוחק קובץ <xliff:g id="COUNT_0">%1$d</xliff:g>.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"בטל"</string>
<string name="copy_preparing" msgid="3896202461003039386">"מתכונן להעתקה..."</string>
<string name="move_preparing" msgid="2772219441375531410">"מתכונן להעברה…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 0c9cb6f..92e1023 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>個のファイルを移動しています。</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>個のファイルを移動しています。</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>件のファイルを削除しています。</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>件のファイルを削除しています。</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"元に戻す"</string>
<string name="copy_preparing" msgid="3896202461003039386">"コピーの準備をしています…"</string>
<string name="move_preparing" msgid="2772219441375531410">"移動の準備をしています…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index 676476f..9a324ba 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">გადაადგილდება <xliff:g id="COUNT_1">%1$d</xliff:g> ფაილი.</item>
<item quantity="one">გადაადგილდება <xliff:g id="COUNT_0">%1$d</xliff:g> ფაილი.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">მიმდინარეობს <xliff:g id="COUNT_1">%1$d</xliff:g> ფაილის წაშლა.</item>
+ <item quantity="one">მიმდინარეობს <xliff:g id="COUNT_0">%1$d</xliff:g> ფაილის წაშლა.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"დაბრუნება"</string>
<string name="copy_preparing" msgid="3896202461003039386">"მომზადება კოპირებისთვის…"</string>
<string name="move_preparing" msgid="2772219441375531410">"გადაადგილება მზადდება..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index 7655a2d..66f69f5 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> файл орын ауыстыруда.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> файл орын ауыстыруда.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> файл жойылуда.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> файл жойылуда.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Кері қайтару"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Көшіруге дайындау…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Тасымалдауға дайындалуда..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index bec2980..5249d97 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">កំពុងផ្លាស់ទីឯកសារ <xliff:g id="COUNT_1">%1$d</xliff:g>។</item>
<item quantity="one">កំពុងផ្លាស់ទីឯកសារ <xliff:g id="COUNT_0">%1$d</xliff:g>។</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">កំពុងលុបឯកសារ <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">កំពុងលុបឯកសារ <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"មិនធ្វើវិញ"</string>
<string name="copy_preparing" msgid="3896202461003039386">"កំពុងរៀបចំចម្លង…"</string>
<string name="move_preparing" msgid="2772219441375531410">"កំពុងរៀបចំផ្លាស់ទី…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index 121197e..d5eda84 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಸರಿಸಲಾಗುತ್ತಿದೆ.</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಸರಿಸಲಾಗುತ್ತಿದೆ.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ.</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"ರದ್ದುಗೊಳಿಸಿ"</string>
<string name="copy_preparing" msgid="3896202461003039386">"ನಕಲಿಸಲು ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ..."</string>
<string name="move_preparing" msgid="2772219441375531410">"ಸರಿಸಲು ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index 0afb855..77a0df6 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">파일 <xliff:g id="COUNT_1">%1$d</xliff:g>개 이동</item>
<item quantity="one">파일 <xliff:g id="COUNT_0">%1$d</xliff:g>개 이동</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">파일 <xliff:g id="COUNT_1">%1$d</xliff:g>개 삭제</item>
+ <item quantity="one">파일 <xliff:g id="COUNT_0">%1$d</xliff:g>개 삭제</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"실행취소"</string>
<string name="copy_preparing" msgid="3896202461003039386">"사본 준비 중…"</string>
<string name="move_preparing" msgid="2772219441375531410">"이동 준비 중…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index 1b8b60e..14a6331 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> файл жылдырылууда.</item>
<item quantity="one"> <xliff:g id="COUNT_0">%1$d</xliff:g> файл жылдырылууда.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> файл жок кылынууда.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> файл жок кылынууда.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Артка кайтаруу"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Көчүрүүгө даярдалууда…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Жылдырууга даярдалууда…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index 21aae6b..2d099f4 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">ກຳລັງຍ້າຍ <xliff:g id="COUNT_1">%1$d</xliff:g> ໄຟລ໌.</item>
<item quantity="one">ກຳລັງຍ້າຍ <xliff:g id="COUNT_0">%1$d</xliff:g> ໄຟລ໌.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">ກຳລັງລຶບ <xliff:g id="COUNT_1">%1$d</xliff:g> ໄຟລ໌.</item>
+ <item quantity="one">ກຳລັງລຶບ <xliff:g id="COUNT_0">%1$d</xliff:g> ໄຟລ໌.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"ບໍ່ເຮັດ"</string>
<string name="copy_preparing" msgid="3896202461003039386">"ກຳລັງກຽມອັດສຳເນົາ…"</string>
<string name="move_preparing" msgid="2772219441375531410">"ກຳລັງກະກຽມຍ້າຍ…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index df1b98a..7b458add 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -77,6 +77,13 @@
<item quantity="many">Perkeliama <xliff:g id="COUNT_1">%1$d</xliff:g> failo.</item>
<item quantity="other">Perkeliama <xliff:g id="COUNT_1">%1$d</xliff:g> failų.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Trinamas <xliff:g id="COUNT_1">%1$d</xliff:g> failas.</item>
+ <item quantity="few">Trinami <xliff:g id="COUNT_1">%1$d</xliff:g> failai.</item>
+ <item quantity="many">Trinama <xliff:g id="COUNT_1">%1$d</xliff:g> failo.</item>
+ <item quantity="other">Trinama <xliff:g id="COUNT_1">%1$d</xliff:g> failų.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Anuliuoti"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Ruošiamasi kopijuoti…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Ruošiamasi perkelti…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index baf306e..44909ce 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -75,6 +75,12 @@
<item quantity="one">Notiek <xliff:g id="COUNT_1">%1$d</xliff:g> faila pārvietošana.</item>
<item quantity="other">Notiek <xliff:g id="COUNT_1">%1$d</xliff:g> failu pārvietošana.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="zero">Notiek <xliff:g id="COUNT_1">%1$d</xliff:g> failu dzēšana.</item>
+ <item quantity="one">Notiek <xliff:g id="COUNT_1">%1$d</xliff:g> faila dzēšana.</item>
+ <item quantity="other">Notiek <xliff:g id="COUNT_1">%1$d</xliff:g> failu dzēšana.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Atsaukt"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Gatavošanās kopēšanai…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Sagatavošana pārvietošanai…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index 93d9bea..7408d0b 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">Се преместува <xliff:g id="COUNT_1">%1$d</xliff:g> датотека.</item>
<item quantity="other">Се преместуваат <xliff:g id="COUNT_1">%1$d</xliff:g> датотеки.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Се брише <xliff:g id="COUNT_1">%1$d</xliff:g> датотека.</item>
+ <item quantity="other">Се бришат <xliff:g id="COUNT_1">%1$d</xliff:g> датотеки.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Врати"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Се подготвува за копирање…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Се подготвува за преместување…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index 162991a..af21db9 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ഫയലുകൾ നീക്കുന്നു.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ഫയൽ നീക്കുന്നു.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ഫയലുകൾ ഇല്ലാതാക്കുന്നു.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ഫയൽ ഇല്ലാതാക്കുന്നു.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"പഴയപടിയാക്കുക"</string>
<string name="copy_preparing" msgid="3896202461003039386">"പകർപ്പിനായി തയ്യാറെടുക്കുന്നു…"</string>
<string name="move_preparing" msgid="2772219441375531410">"നീക്കാനൊരുങ്ങുന്നു…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index 9276964..1475e08 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> файл зөөж байна.</item>
<item quantity="one"> <xliff:g id="COUNT_0">%1$d</xliff:g> файл зөөж байна.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> файлыг устгаж байна.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> файлыг устгаж байна.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Буцаах"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Хуулбарлахад бэлтгэж байна..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Зөөвөрлөхөд бэлтгэж байна..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index 441035d..b7654881 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> फाईल हलवित आहे.</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फायली हलवित आहे.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> फाईल हटवित आहे.</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फायली हटवित आहे.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"पूर्ववत करा"</string>
<string name="copy_preparing" msgid="3896202461003039386">"कॉपी करण्यासाठी तयार करीत आहे…"</string>
<string name="move_preparing" msgid="2772219441375531410">"हलविण्यास तयार होत आहे…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 89a738c..4682957 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Mengalihkan <xliff:g id="COUNT_1">%1$d</xliff:g> fail.</item>
<item quantity="one">Mengalihkan <xliff:g id="COUNT_0">%1$d</xliff:g> fail.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Memadam <xliff:g id="COUNT_1">%1$d</xliff:g> fail.</item>
+ <item quantity="one">Memadam <xliff:g id="COUNT_0">%1$d</xliff:g> fail.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Buat asal"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Bersedia untuk salin..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Bersedia untuk mengalih…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index a7d740a..a422898 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ဖိုင် ရွှေ့နေစဉ်</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ဖိုင် ရွှေ့နေစဉ်</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">ဖိုင် <xliff:g id="COUNT_1">%1$d</xliff:g> ခုကိုဖျက်နေသည်။</item>
+ <item quantity="one">ဖိုင် <xliff:g id="COUNT_0">%1$d</xliff:g> ခုကိုဖျက်နေသည်။</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"ပြန်ဖျက်ရန်"</string>
<string name="copy_preparing" msgid="3896202461003039386">"မိတ္တူကူးရန်ပြင်ဆင်နေ..."</string>
<string name="move_preparing" msgid="2772219441375531410">"ရွှေ့ရန် ပြင်ဆင်နေသည်…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index 09b75c0..5bcc50f 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Flytter <xliff:g id="COUNT_1">%1$d</xliff:g> filer.</item>
<item quantity="one">Flytter <xliff:g id="COUNT_0">%1$d</xliff:g> fil.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Sletter <xliff:g id="COUNT_1">%1$d</xliff:g> filer.</item>
+ <item quantity="one">Sletter <xliff:g id="COUNT_0">%1$d</xliff:g> fil.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Angre"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Forbereder kopiering …"</string>
<string name="move_preparing" msgid="2772219441375531410">"Forbereder flytting …"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index 8a73bad..b121035 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फाइलहरू सार्दै।</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> फाइलहरु सार्दै।</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फाइलहरू मेट्दै।</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> फाइल मेट्दै।</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"अनडू गर्नुहोस्"</string>
<string name="copy_preparing" msgid="3896202461003039386">"प्रतिलिपिको लागि तयारी गर्दै ..."</string>
<string name="move_preparing" msgid="2772219441375531410">"सार्नको लागि तयारी गर्दै ..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index 75c35cb..88ef0bf 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> bestanden verplaatsen.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> bestand verplaatsen.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> bestanden verwijderen.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> bestand verwijderen.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Ongedaan maken"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Kopiëren voorbereiden…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Verplaatsen voorbereiden…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index 18d9bc3..a55fc27 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਨੂੰ ਮੂਵ ਕਰਨਾ।</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਨੂੰ ਮੂਵ ਕਰਨਾ।</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਮਿਟਾ ਰਿਹਾ ਹੈ।</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਮਿਟਾ ਰਿਹਾ ਹੈ।</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"ਪਹਿਲਾਂ ਵਰਗਾ ਕਰੋ"</string>
<string name="copy_preparing" msgid="3896202461003039386">"ਕਾਪੀ ਲਈ ਤਿਆਰ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="move_preparing" msgid="2772219441375531410">"ਮੂਵ ਲਈ ਤਿਆਰ ਕਰ ਰਿਹਾ ਹੈ..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 0d38933..0099e5a 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -77,6 +77,13 @@
<item quantity="other">Przenoszę <xliff:g id="COUNT_1">%1$d</xliff:g> pliku.</item>
<item quantity="one">Przenoszę <xliff:g id="COUNT_0">%1$d</xliff:g> plik.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="few">Usuwam <xliff:g id="COUNT_1">%1$d</xliff:g> pliki.</item>
+ <item quantity="many">Usuwam <xliff:g id="COUNT_1">%1$d</xliff:g> plików.</item>
+ <item quantity="other">Usuwam <xliff:g id="COUNT_1">%1$d</xliff:g> pliku.</item>
+ <item quantity="one">Usuwam <xliff:g id="COUNT_0">%1$d</xliff:g> plik.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Cofnij"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Przygotowuję do kopiowania…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Przygotowuję przenoszenie…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index a23ecd0..18506dc 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">Movendo <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos.</item>
<item quantity="other">Movendo <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Excluindo <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos.</item>
+ <item quantity="other">Excluindo <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Desfazer"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Preparando para copiar..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Preparando para mover..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index 3b8ec6a..adf6ec9 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">A mover <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros.</item>
<item quantity="one">A mover <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">A eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros.</item>
+ <item quantity="one">A eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Anular"</string>
<string name="copy_preparing" msgid="3896202461003039386">"A preparar para copiar…"</string>
<string name="move_preparing" msgid="2772219441375531410">"A preparar para mover…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index a23ecd0..18506dc 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">Movendo <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos.</item>
<item quantity="other">Movendo <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Excluindo <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos.</item>
+ <item quantity="other">Excluindo <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Desfazer"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Preparando para copiar..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Preparando para mover..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index 692fd5a..1b046a3 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -75,6 +75,12 @@
<item quantity="other">Se mută <xliff:g id="COUNT_1">%1$d</xliff:g> de fișiere.</item>
<item quantity="one">Se mută <xliff:g id="COUNT_0">%1$d</xliff:g> fișier.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="few">Se șterg <xliff:g id="COUNT_1">%1$d</xliff:g> fișiere.</item>
+ <item quantity="other">Se șterg <xliff:g id="COUNT_1">%1$d</xliff:g> de fișiere.</item>
+ <item quantity="one">Se șterge <xliff:g id="COUNT_0">%1$d</xliff:g> fișier.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Anulați"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Se pregătește copierea..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Se pregătește mutarea…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index 2e5840d..84ae160 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -77,6 +77,13 @@
<item quantity="many">Перемещение <xliff:g id="COUNT_1">%1$d</xliff:g> файлов...</item>
<item quantity="other">Перемещение <xliff:g id="COUNT_1">%1$d</xliff:g> файла...</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Удаление <xliff:g id="COUNT_1">%1$d</xliff:g> файла…</item>
+ <item quantity="few">Удаление <xliff:g id="COUNT_1">%1$d</xliff:g> файлов…</item>
+ <item quantity="many">Удаление <xliff:g id="COUNT_1">%1$d</xliff:g> файлов…</item>
+ <item quantity="other">Удаление <xliff:g id="COUNT_1">%1$d</xliff:g> файла…</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Отменить"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Подготовка к копированию…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Подготовка…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index 2bd7585..d9d5818 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g> ක් ගෙන යමින්.</item>
<item quantity="other">ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g> ක් ගෙන යමින්.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g>ක් මකමින්.</item>
+ <item quantity="other">ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g>ක් මකමින්.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"අස් කරන්න"</string>
<string name="copy_preparing" msgid="3896202461003039386">"පිටපතක් සඳහා සූදානම් කරමින්..."</string>
<string name="move_preparing" msgid="2772219441375531410">"ගෙන යාම සඳහා පිළියෙළ කරමින් ..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index e14362d..e0ff5fc 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -77,6 +77,13 @@
<item quantity="other">Presúva sa <xliff:g id="COUNT_1">%1$d</xliff:g> súborov.</item>
<item quantity="one">Presúva sa <xliff:g id="COUNT_0">%1$d</xliff:g> súbor.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="few">Odstraňujú sa <xliff:g id="COUNT_1">%1$d</xliff:g> súbory.</item>
+ <item quantity="many">Odstraňuje sa <xliff:g id="COUNT_1">%1$d</xliff:g> súboru.</item>
+ <item quantity="other">Odstraňuje sa <xliff:g id="COUNT_1">%1$d</xliff:g> súborov.</item>
+ <item quantity="one">Odstraňuje sa <xliff:g id="COUNT_0">%1$d</xliff:g> súbor.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Späť"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Pripravuje sa na kopírovanie..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Prebieha príprava na presunutie…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index a0b3737..83e5124 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -77,6 +77,13 @@
<item quantity="few">Premikanje <xliff:g id="COUNT_1">%1$d</xliff:g> datotek.</item>
<item quantity="other">Premikanje <xliff:g id="COUNT_1">%1$d</xliff:g> datotek.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Izbris <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke</item>
+ <item quantity="two">Izbris <xliff:g id="COUNT_1">%1$d</xliff:g> datotek</item>
+ <item quantity="few">Izbris <xliff:g id="COUNT_1">%1$d</xliff:g> datotek</item>
+ <item quantity="other">Izbris <xliff:g id="COUNT_1">%1$d</xliff:g> datotek</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Razveljavi"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Pripravljanje na kopiranje …"</string>
<string name="move_preparing" msgid="2772219441375531410">"Priprava na premikanje …"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index 796c2ea..981daf2 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Po zhvendos <xliff:g id="COUNT_1">%1$d</xliff:g> skedarë.</item>
<item quantity="one">Po zhvendos <xliff:g id="COUNT_0">%1$d</xliff:g> skedar.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Po fshin <xliff:g id="COUNT_1">%1$d</xliff:g> skedarë.</item>
+ <item quantity="one">Po fshin <xliff:g id="COUNT_0">%1$d</xliff:g> skedar.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Zhbëj"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Po përgatitet për kopjimin…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Po përgatitet për zhvendosjen…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index ee8d495..bd0e9af 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -75,6 +75,12 @@
<item quantity="few">Премештају се <xliff:g id="COUNT_1">%1$d</xliff:g> датотеке.</item>
<item quantity="other">Премешта се <xliff:g id="COUNT_1">%1$d</xliff:g> датотека.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Брише се <xliff:g id="COUNT_1">%1$d</xliff:g> датотека.</item>
+ <item quantity="few">Бришу се <xliff:g id="COUNT_1">%1$d</xliff:g> датотеке.</item>
+ <item quantity="other">Брише се <xliff:g id="COUNT_1">%1$d</xliff:g> датотека.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Опозови"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Припрема се копирање…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Припрема се премештање..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index d0a4bbc..2569e00 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Flyttar <xliff:g id="COUNT_1">%1$d</xliff:g> filer.</item>
<item quantity="one">Flyttar <xliff:g id="COUNT_0">%1$d</xliff:g> fil.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Raderar <xliff:g id="COUNT_1">%1$d</xliff:g> filer.</item>
+ <item quantity="one">Raderar <xliff:g id="COUNT_0">%1$d</xliff:g> fil.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Ångra"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Kopieringen förbereds …"</string>
<string name="move_preparing" msgid="2772219441375531410">"Förbereder för att flytta …"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index ae9503f..ce186d7 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Inahamisha faili <xliff:g id="COUNT_1">%1$d</xliff:g>.</item>
<item quantity="one">Inahamisha faili <xliff:g id="COUNT_0">%1$d</xliff:g>.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Inafuta faili <xliff:g id="COUNT_1">%1$d</xliff:g>.</item>
+ <item quantity="one">Inafuta faili <xliff:g id="COUNT_0">%1$d</xliff:g>.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Tendua"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Inaanda kunakili..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Inatayarisha kuhamisha..."</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
index 0b03a94..f4bc88e 100644
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/styles.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="DialogWhenReallyLarge" parent="@*android:style/Theme.Material.DayNight.Dialog">
+ <style name="DocumentsBaseTheme" parent="@*android:style/Theme.Material.DayNight.Dialog">
<!-- We do not specify width of window here because the max size of
floating window specified by windowFixedWidthis is limited. -->
<item name="*android:windowFixedHeightMajor">80%</item>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index 30289d0..b7b9c09 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> கோப்புகளை நகர்த்துகிறது.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> கோப்பை நகர்த்துகிறது.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> கோப்புகளை நீக்குகிறது.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> கோப்பை நீக்குகிறது.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"செயல்தவிர்"</string>
<string name="copy_preparing" msgid="3896202461003039386">"நகல் தயாராகிறது…"</string>
<string name="move_preparing" msgid="2772219441375531410">"நகர்த்துவதற்குத் தயார்படுத்துகிறது…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index 46a4114..7a81486 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ఫైల్లను తరలిస్తోంది.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఫైల్ను తరలిస్తోంది.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ఫైల్లను తొలగిస్తోంది.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఫైల్ను తొలగిస్తోంది.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"చర్య రద్దు చేయి"</string>
<string name="copy_preparing" msgid="3896202461003039386">"కాపీ చేయడానికి సిద్ధం చేస్తోంది…"</string>
<string name="move_preparing" msgid="2772219441375531410">"తరలించడానికి సిద్ధమవుతోంది…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index f6e96f7..87c0a73 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">กำลังย้าย <xliff:g id="COUNT_1">%1$d</xliff:g> ไฟล์</item>
<item quantity="one">กำลังย้าย <xliff:g id="COUNT_0">%1$d</xliff:g> ไฟล์</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">กำลังลบ <xliff:g id="COUNT_1">%1$d</xliff:g> ไฟล์</item>
+ <item quantity="one">กำลังลบ <xliff:g id="COUNT_0">%1$d</xliff:g> ไฟล์</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"เลิกทำ"</string>
<string name="copy_preparing" msgid="3896202461003039386">"กำลังเตรียมการคัดลอก…"</string>
<string name="move_preparing" msgid="2772219441375531410">"กำลังเตรียมการย้าย…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index 6da7eb9..eaef936 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">Inililipat ang <xliff:g id="COUNT_1">%1$d</xliff:g> file.</item>
<item quantity="other">Inililipat ang <xliff:g id="COUNT_1">%1$d</xliff:g> na file.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Dine-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> file.</item>
+ <item quantity="other">Dine-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> na file.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"I-undo"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Naghahanda para sa pagkopya…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Naghahanda para sa paglilipat…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index 15f70b6..8c0596f 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dosya taşınıyor.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dosya taşınıyor.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dosya siliniyor.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dosya siliniyor.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Geri al"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Kopyalanmak için hazırlanıyor…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Taşıma için hazırlanıyor…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index 5bf37f4..9bbc59a 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -77,6 +77,13 @@
<item quantity="many">Переміщення <xliff:g id="COUNT_1">%1$d</xliff:g> файлів.</item>
<item quantity="other">Переміщення <xliff:g id="COUNT_1">%1$d</xliff:g> файла.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Видалення <xliff:g id="COUNT_1">%1$d</xliff:g> файлу.</item>
+ <item quantity="few">Видалення <xliff:g id="COUNT_1">%1$d</xliff:g> файлів.</item>
+ <item quantity="many">Видалення <xliff:g id="COUNT_1">%1$d</xliff:g> файлів.</item>
+ <item quantity="other">Видалення <xliff:g id="COUNT_1">%1$d</xliff:g> файлу.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Відмінити"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Підготовка до копіювання…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Підготовка до переміщення…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 008366e..0c12aa1 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> فائلز منتقل کی جا رہی ہیں۔</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> فائل منتقل کی جا رہی ہے۔</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> فائلیں حذف ہو رہی ہیں۔</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> فائل حذف ہو رہی ہے۔</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"کالعدم کریں"</string>
<string name="copy_preparing" msgid="3896202461003039386">"کاپی کیلئے تیار ہو رہا ہے…"</string>
<string name="move_preparing" msgid="2772219441375531410">"منتقلی کیلئے تیار ہو رہی ہیں…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index ad5b115b..ec91885 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta fayl ko‘chirib o‘tkazilmoqda.</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta fayl ko‘chirib o‘tkazilmoqda.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta fayl o‘chirilmoqda.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta fayl o‘chirilmoqda.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Bekor qilish"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Nuxsa olishga tayyorgarlik..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Ko‘chirishga tayyorgarlik…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index 6c4d2a5..a652ecc 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">Đang di chuyển <xliff:g id="COUNT_1">%1$d</xliff:g> tệp.</item>
<item quantity="one">Đang di chuyển <xliff:g id="COUNT_0">%1$d</xliff:g> tệp.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">Xóa <xliff:g id="COUNT_1">%1$d</xliff:g> tệp.</item>
+ <item quantity="one">Xóa <xliff:g id="COUNT_0">%1$d</xliff:g> tệp.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Hoàn tác"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Đang chuẩn bị sao chép…"</string>
<string name="move_preparing" msgid="2772219441375531410">"Đang chuẩn bị di chuyển…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index fbd11ad..b0f5480 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">正在移动 <xliff:g id="COUNT_1">%1$d</xliff:g> 个文件。</item>
<item quantity="one">正在移动 <xliff:g id="COUNT_0">%1$d</xliff:g> 个文件。</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">正在删除 <xliff:g id="COUNT_1">%1$d</xliff:g> 个文件。</item>
+ <item quantity="one">正在删除 <xliff:g id="COUNT_0">%1$d</xliff:g> 个文件。</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"撤消"</string>
<string name="copy_preparing" msgid="3896202461003039386">"正在准备复制…"</string>
<string name="move_preparing" msgid="2772219441375531410">"正在准备移动…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index 4970338..220b716 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">正在轉移 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案。</item>
<item quantity="one">正在轉移 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案。</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">正在刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案。</item>
+ <item quantity="one">正在刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案。</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"復原"</string>
<string name="copy_preparing" msgid="3896202461003039386">"正在準備複製…"</string>
<string name="move_preparing" msgid="2772219441375531410">"正在準備移動…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index 42c86dc..9c21aa5 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -73,6 +73,11 @@
<item quantity="other">正在移動 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案。</item>
<item quantity="one">正在移動 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案。</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="other">正在刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案。</item>
+ <item quantity="one">正在刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案。</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"復原"</string>
<string name="copy_preparing" msgid="3896202461003039386">"正在準備複製…"</string>
<string name="move_preparing" msgid="2772219441375531410">"準備移動…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index 3f09790..5ce9f12 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -73,6 +73,11 @@
<item quantity="one">Ihambisa amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>.</item>
<item quantity="other">Ihambisa amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>.</item>
</plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Isusa amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>.</item>
+ <item quantity="other">Isusa amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Hlehlisa"</string>
<string name="copy_preparing" msgid="3896202461003039386">"Ilungiselela ukukopisha..."</string>
<string name="move_preparing" msgid="2772219441375531410">"Ilungiselela ukuhambisa…"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
diff --git a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml b/packages/DocumentsUI/res/values/attrs.xml
similarity index 73%
copy from core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
copy to packages/DocumentsUI/res/values/attrs.xml
index 26bc8ad..0afc3a2 100644
--- a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
+++ b/packages/DocumentsUI/res/values/attrs.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 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,7 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<pathInterpolator
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0.0,0.0 c 0.33333333,0.0 0.0,1.0 1.0,1.0" />
+<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 0735ff9..cb6957d 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -19,14 +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 616f4dd..4b44d7c 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -139,6 +139,13 @@
<item quantity="one">Moving <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
<item quantity="other">Moving <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
</plurals>
+ <!-- Text shown when files are deleted -->
+ <plurals name="deleting">
+ <item quantity="one">Deleting <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
+ <item quantity="other">Deleting <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
+ </plurals>
+ <!-- Text shown for the undo button -->
+ <string name="undo">Undo</string>
<!-- Text shown on the notification while DocumentsUI performs setup in preparation for copying files [CHAR LIMIT=32] -->
<string name="copy_preparing">Preparing for copy\u2026</string>
<!-- Text shown on the notification while DocumentsUI performs setup in preparation for moving files [CHAR LIMIT=32] -->
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index fa94ff1..8301816 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -16,16 +16,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="DialogWhenReallyLarge" parent="@android:style/Theme.Material.DayNight.DarkActionBar" />
+ <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar" />
+ <style name="ActionBarTheme" parent="@*android:style/ThemeOverlay.Material.Dark.ActionBar" />
+ <style name="ActionBarPopupTheme" parent="@*android:style/ThemeOverlay.Material.Light" />
- <style name="DocumentsTheme" parent="@style/DialogWhenReallyLarge">
- <item name="android:actionBarWidgetTheme">@null</item>
- <item name="android:actionBarTheme">@*android:style/ThemeOverlay.Material.Dark.ActionBar</item>
- <item name="android:actionBarPopupTheme">@*android:style/ThemeOverlay.Material.Light</item>
+ <style name="DocumentsTheme" parent="@style/DocumentsBaseTheme">
+ <item name="actionBarWidgetTheme">@null</item>
+ <item name="actionBarTheme">@style/ActionBarTheme</item>
+ <item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
- <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_900</item>
- <item name="android:colorPrimary">@*android:color/material_blue_grey_800</item>
- <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
+ <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>
@@ -37,14 +40,11 @@
<item name="android:alertDialogTheme">@android:style/Theme.Material.Light.Dialog.Alert</item>
</style>
- <style name="DocumentsNonDialogTheme" parent="@android:style/Theme.Material.DayNight.DarkActionBar">
- <item name="android:actionBarWidgetTheme">@null</item>
- <item name="android:actionBarTheme">@*android:style/ThemeOverlay.Material.Dark.ActionBar</item>
- <item name="android:actionBarPopupTheme">@*android:style/ThemeOverlay.Material.Light</item>
-
- <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_900</item>
- <item name="android:colorPrimary">@*android:color/material_blue_grey_800</item>
- <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
+ <style name="DocumentsBaseTheme.FullScreen" parent="@style/Theme.AppCompat.Light.DarkActionBar">
+ <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>
@@ -55,30 +55,36 @@
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
</style>
+ <style name="DocumentsNonDialogTheme" parent="@style/DocumentsBaseTheme.FullScreen">
+ <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">
<item name="android:background">@color/material_grey_600</item>
</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="@android:style/Theme.Material.DayNight.DarkActionBar">
- <item name="android:actionBarWidgetTheme">@null</item>
+ <style name="FilesTheme" parent="@style/DocumentsBaseTheme.FullScreen">
+ <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="android:actionModeStyle">@style/FilesActionModeStyle</item>
+ <item name="colorActionMode">@color/platform_blue_700</item>
- <item name="android:colorPrimaryDark">@color/status_bar_background</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:listDivider">@*android:drawable/list_divider_material</item>
-
- <item name="android:windowActionBar">false</item>
- <item name="android:windowActionModeOverlay">true</item>
- <item name="android:windowNoTitle">true</item>
-
- <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</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>
+
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 2835106..1e7a42f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -16,6 +16,13 @@
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.DirectoryFragment.ANIM_SIDE;
@@ -47,7 +54,6 @@
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;
@@ -85,7 +91,6 @@
private int mLayoutId;
private final String mTag;
- public abstract State getDisplayState();
public abstract void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
@@ -93,7 +98,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;
@@ -106,7 +111,7 @@
mState = (icicle != null)
? icicle.<State>getParcelable(EXTRA_STATE)
- : buildDefaultState();
+ : buildState();
setContentView(mLayoutId);
@@ -154,30 +159,46 @@
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
+ grid.setVisible(state.derivedMode != State.MODE_GRID);
+ list.setVisible(state.derivedMode != State.MODE_LIST);
+ settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
+
return shown;
}
+ State buildDefaultState() {
+ State state = new State();
+
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+
+ 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);
+
+ state.excludedAuthorities = getExcludedAuthorities();
+
+ return state;
+ }
+
void onStackRestored(boolean restored, boolean external) {}
void onRootPicked(RootInfo root) {
@@ -344,6 +365,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 =
@@ -664,6 +689,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 8b92331..f8ec8f1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -56,6 +56,7 @@
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";
@@ -159,6 +160,7 @@
// Catch-all to prevent any copy errors from wedging the app.
Log.e(TAG, "Exceptions occurred during copying", e);
} finally {
+ if (DEBUG) Log.d(TAG, "Cleaning up after copy");
ContentProviderClient.releaseQuietly(mSrcClient);
ContentProviderClient.releaseQuietly(mDstClient);
@@ -166,10 +168,12 @@
mNotificationManager.cancel(mJobId, 0);
if (mFailedFiles.size() > 0) {
+ Log.e(TAG, mFailedFiles.size() + " files failed to copy");
final Context context = getApplicationContext();
final Intent navigateIntent = new Intent(context, FilesActivity.class);
navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack);
navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY);
+ navigateIntent.putExtra(EXTRA_TRANSFER_MODE, transferMode);
navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, mFailedFiles);
final int titleResourceId = (transferMode == TRANSFER_MODE_COPY ?
@@ -186,6 +190,7 @@
.setAutoCancel(true);
mNotificationManager.notify(mJobId, 0, errorBuilder.build());
}
+ if (DEBUG) Log.d(TAG, "Done cleaning up");
}
}
@@ -398,6 +403,9 @@
*/
private void copy(DocumentInfo srcInfo, DocumentInfo dstDirInfo, int mode)
throws RemoteException {
+ if (DEBUG) Log.d(TAG, "Copying " + srcInfo.displayName + " (" + srcInfo.derivedUri + ")" +
+ " to " + dstDirInfo.displayName + " (" + dstDirInfo.derivedUri + ")");
+
final Uri dstUri = DocumentsContract.createDocument(mDstClient, dstDirInfo.derivedUri,
srcInfo.mimeType, srcInfo.displayName);
if (dstUri == null) {
@@ -499,10 +507,28 @@
srcFile.checkError();
} catch (IOException e) {
copyError = e;
+
try {
- dstFile.closeWithError(copyError.getMessage());
- } catch (IOException closeError) {
- Log.e(TAG, "Error closing destination", closeError);
+ DocumentInfo info = DocumentInfo.fromUri(getContentResolver(), srcUri);
+ mFailedFiles.add(info);
+ Log.e(TAG, "Error while copying " + info.displayName + " (" + info.derivedUri + ")",
+ copyError);
+ } catch (FileNotFoundException ignore) {
+ // Generate a dummy DocumentInfo so an error still gets reflected in the UI for this
+ // file.
+ DocumentInfo info = new DocumentInfo();
+ info.derivedUri = srcUri;
+ info.displayName = "Unknown [" + srcUri + "]";
+ mFailedFiles.add(info);
+ Log.e(TAG, "Error while copying " + srcUri, copyError);
+ }
+
+ if (dstFile != null) {
+ try {
+ dstFile.closeWithError(copyError.getMessage());
+ } catch (IOException closeError) {
+ Log.e(TAG, "Error closing destination", closeError);
+ }
}
} finally {
// This also ensures the file descriptors are closed.
@@ -510,16 +536,6 @@
IoUtils.closeQuietly(dst);
}
- if (copyError != null) {
- // Log errors.
- Log.e(TAG, "Error while copying " + srcUri.toString(), copyError);
- try {
- mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri));
- } catch (FileNotFoundException ignore) {
- Log.w(TAG, "Source file gone: " + srcUri, copyError);
- // The source file is gone.
- }
- }
if (copyError != null || mIsCancelled) {
// Clean up half-copied files.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index c28806b..046f3df 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -61,6 +61,9 @@
import android.os.SystemProperties;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.design.widget.Snackbar;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -73,6 +76,8 @@
import android.text.format.Time;
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;
@@ -83,6 +88,7 @@
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@@ -96,7 +102,6 @@
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;
@@ -129,6 +134,8 @@
private static final String EXTRA_QUERY = "query";
private static final String EXTRA_IGNORE_STATE = "ignoreState";
+ private Model mModel;
+
private final Handler mHandler = new Handler(Looper.getMainLooper());
private View mEmptyView;
@@ -147,7 +154,6 @@
private LoaderCallbacks<DirectoryResult> mCallbacks;
private FragmentTuner mFragmentTuner;
private DocumentClipper mClipper;
- private MultiSelectManager mSelectionManager;
// These are lazily initialized.
private LinearLayoutManager mListLayout;
private GridLayoutManager mGridLayout;
@@ -261,7 +267,7 @@
}
// Clear any outstanding selection
- mSelectionManager.clearSelection();
+ mModel.clearSelection();
}
@Override
@@ -290,14 +296,20 @@
}
};
- mSelectionManager = new MultiSelectManager(
+ // TODO: instead of inserting the view into the constructor, extract listener-creation code
+ // and set the listener on the view after the fact. Then the view doesn't need to be passed
+ // into the selection manager which is passed into the model.
+ MultiSelectManager selMgr= new MultiSelectManager(
mRecView,
listener,
state.allowMultiple
? MultiSelectManager.MODE_MULTIPLE
: MultiSelectManager.MODE_SINGLE);
+ selMgr.addCallback(new SelectionModeListener());
- mSelectionManager.addCallback(new SelectionModeListener());
+ mModel = new Model(context, selMgr);
+ mModel.setSelectionManager(selMgr);
+ mModel.addUpdateListener(mAdapter);
mType = getArguments().getInt(EXTRA_TYPE);
mStateKey = buildStateKey(root, doc);
@@ -367,7 +379,7 @@
if (!isAdded()) return;
- mAdapter.replaceResult(result);
+ mModel.update(result);
// Push latest state up to UI
// TODO: if mode change was racing with us, don't overwrite it
@@ -380,7 +392,7 @@
updateDisplayState();
// When launched into empty recents, show drawer
- if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched &&
+ if (mType == TYPE_RECENT_OPEN && mModel.isEmpty() && !state.stackTouched &&
context instanceof DocumentsActivity) {
((DocumentsActivity) context).setRootsDrawerOpen(true);
}
@@ -398,7 +410,7 @@
@Override
public void onLoaderReset(Loader<DirectoryResult> loader) {
- mAdapter.replaceResult(null);
+ mModel.update(null);
}
};
@@ -433,7 +445,7 @@
}
private boolean onSingleTapUp(MotionEvent e) {
- if (Events.isTouchEvent(e) && mSelectionManager.getSelection().isEmpty()) {
+ if (Events.isTouchEvent(e) && mModel.getSelection().isEmpty()) {
int position = getEventAdapterPosition(e);
if (position != RecyclerView.NO_POSITION) {
return handleViewItem(position);
@@ -454,14 +466,14 @@
}
private boolean handleViewItem(int position) {
- final Cursor cursor = mAdapter.getItem(position);
+ final Cursor cursor = mModel.getItem(position);
checkNotNull(cursor, "Cursor cannot be null.");
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
if (isDocumentEnabled(docMimeType, docFlags)) {
final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
- ((BaseActivity) getActivity()).onDocumentPicked(doc, mAdapter);
- mSelectionManager.clearSelection();
+ ((BaseActivity) getActivity()).onDocumentPicked(doc, mModel);
+ mModel.clearSelection();
return true;
}
return false;
@@ -565,6 +577,9 @@
}
mRecView.setLayoutManager(layout);
+ // TODO: Once b/23691541 is resolved, use a listener within MultiSelectManager instead of
+ // imperatively calling this function.
+ mModel.mSelectionManager.handleLayoutChanged();
// setting layout manager automatically invalidates existing ViewHolders.
mThumbSize = new Point(thumbSize, thumbSize);
}
@@ -598,7 +613,7 @@
public boolean onBeforeItemStateChange(int position, boolean selected) {
// Directories and footer items cannot be checked
if (selected) {
- final Cursor cursor = mAdapter.getItem(position);
+ final Cursor cursor = mModel.getItem(position);
checkNotNull(cursor, "Cursor cannot be null.");
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
@@ -610,7 +625,7 @@
@Override
public void onItemStateChanged(int position, boolean selected) {
- final Cursor cursor = mAdapter.getItem(position);
+ final Cursor cursor = mModel.getItem(position);
checkNotNull(cursor, "Cursor cannot be null.");
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
@@ -621,24 +636,26 @@
@Override
public void onSelectionChanged() {
- mSelectionManager.getSelection(mSelected);
+ 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()));
@@ -651,7 +668,7 @@
if (DEBUG) Log.d(TAG, "Handling action mode destroyed.");
mActionMode = null;
// clear selection
- mSelectionManager.clearSelection();
+ mModel.clearSelection();
mSelected.clear();
mNoDeleteCount = 0;
}
@@ -659,8 +676,8 @@
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
- mode.setTitle(TextUtils.formatSelectedCount(mSelectionManager.getSelection().size()));
- return mSelectionManager.getSelection().size() > 0;
+ mode.setTitle(TextUtils.formatSelectedCount(mModel.getSelection().size()));
+ return mModel.getSelection().size() > 0;
}
@Override
@@ -679,8 +696,7 @@
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- Selection selection = new Selection();
- mSelectionManager.getSelection(selection);
+ Selection selection = mModel.getSelection(new Selection());
final int id = item.getItemId();
if (id == R.id.menu_open) {
@@ -793,41 +809,43 @@
}
private void deleteDocuments(final Selection selected) {
- final Context context = getActivity();
- final ContentResolver resolver = context.getContentResolver();
+ Context context = getActivity();
+ ContentResolver resolver = context.getContentResolver();
+ String message = Shared.getQuantityString(context, R.plurals.deleting, selected.size());
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- boolean hadTrouble = false;
- for (DocumentInfo doc : docs) {
- if (!doc.isDeleteSupported()) {
- Log.w(TAG, "Skipping " + doc);
- hadTrouble = true;
- continue;
- }
+ mModel.markForDeletion(selected);
- ContentProviderClient client = null;
- try {
- client = DocumentsApplication.acquireUnstableProviderOrThrow(
- resolver, doc.derivedUri.getAuthority());
- DocumentsContract.deleteDocument(client, doc.derivedUri);
- } catch (Exception e) {
- Log.w(TAG, "Failed to delete " + doc);
- hadTrouble = true;
- } finally {
- ContentProviderClient.releaseQuietly(client);
- }
- }
+ Activity activity = getActivity();
+ Snackbar.make(this.getView(), message, Snackbar.LENGTH_LONG)
+ .setAction(
+ R.string.undo,
+ new android.view.View.OnClickListener() {
+ @Override
+ public void onClick(View view) {}
+ })
+ .setCallback(
+ new Snackbar.Callback() {
+ @Override
+ public void onDismissed(Snackbar snackbar, int event) {
+ if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) {
+ mModel.undoDeletion();
+ } else {
+ mModel.finalizeDeletion(
+ new Runnable() {
+ @Override
+ public void run() {
+ Snackbar.make(
+ DirectoryFragment.this.getView(),
+ R.string.toast_failed_delete,
+ Snackbar.LENGTH_LONG)
+ .show();
- if (hadTrouble) {
- Toast.makeText(
- context,
- R.string.toast_failed_delete,
- Toast.LENGTH_SHORT).show();
- }
- }
- }.execute(selected);
+ }
+ });
+ }
+ }
+ })
+ .show();
}
private void transferDocuments(final Selection selected, final int mode) {
@@ -949,49 +967,31 @@
}
private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder>
- implements DocumentContext {
+ implements Model.UpdateListener {
private final Context mContext;
private final LayoutInflater mInflater;
// TODO: Bring back support for footers.
private final List<Footer> mFooters = new ArrayList<>();
- private Cursor mCursor;
- private int mCursorCount;
-
public DocumentsAdapter(Context context) {
mContext = context;
mInflater = LayoutInflater.from(context);
}
- public void replaceResult(DirectoryResult result) {
- if (DEBUG) Log.i(TAG, "Updating adapter with new result set.");
- mCursor = result != null ? result.cursor : null;
- mCursorCount = mCursor != null ? mCursor.getCount() : 0;
-
+ public void onModelUpdate(Model model) {
mFooters.clear();
-
- final Bundle extras = mCursor != null ? mCursor.getExtras() : null;
- if (extras != null) {
- final String info = extras.getString(DocumentsContract.EXTRA_INFO);
- if (info != null) {
- mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_info, info));
- }
- final String error = extras.getString(DocumentsContract.EXTRA_ERROR);
- if (error != null) {
- mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, error));
- }
- if (extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)) {
- mFooters.add(new LoadingFooter());
- }
+ 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 (result != null && result.exception != null) {
- mFooters.add(new MessageFooter(
- 3, R.drawable.ic_dialog_alert, getString(R.string.query_error)));
- }
-
- if (isEmpty()) {
+ if (model.isEmpty()) {
mEmptyView.setVisibility(View.VISIBLE);
} else {
mEmptyView.setVisibility(View.GONE);
@@ -1000,6 +1000,12 @@
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);
@@ -1025,7 +1031,7 @@
final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
context, mThumbSize);
- final Cursor cursor = getItem(position);
+ final Cursor cursor = mModel.getItem(position);
checkNotNull(cursor, "Cursor cannot be null.");
final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
@@ -1041,7 +1047,7 @@
holder.docId = docId;
final View itemView = holder.view;
- itemView.setActivated(mSelectionManager.getSelection().contains(position));
+ itemView.setActivated(mModel.isSelected(position));
final View line1 = itemView.findViewById(R.id.line1);
final View line2 = itemView.findViewById(R.id.line2);
@@ -1214,44 +1220,21 @@
}
@Override
- public Cursor getCursor() {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- throw new IllegalStateException("Can't call getCursor from non-main thread.");
- }
- return mCursor;
- }
-
- private Cursor getItem(int position) {
- if (position < mCursorCount) {
- mCursor.moveToPosition(position);
- return mCursor;
- }
-
- Log.w(TAG, "Returning null cursor for position: " + position);
- if (DEBUG) Log.d(TAG, "...Adapter size: " + mCursorCount);
- if (DEBUG) Log.d(TAG, "...Footer size: " + mFooters.size());
- return null;
- }
-
- @Override
public int getItemCount() {
- return mCursorCount;
+ return mModel.getItemCount();
// return mCursorCount + mFooters.size();
}
@Override
public int getItemViewType(int position) {
- if (position < mCursorCount) {
+ final int itemCount = mModel.getItemCount();
+ if (position < itemCount) {
return 0;
} else {
- position -= mCursorCount;
+ position -= itemCount;
return mFooters.get(position).getItemViewType();
}
}
-
- private boolean isEmpty() {
- return getItemCount() > 0;
- }
}
private static String formatTime(Context context, long when) {
@@ -1328,27 +1311,6 @@
return MimePredicate.mimeMatches(state.acceptMimes, docMimeType);
}
- private List<DocumentInfo> getSelectedDocuments() {
- Selection sel = mSelectionManager.getSelection(new Selection());
- return getItemsAsDocuments(sel);
- }
-
- private List<DocumentInfo> getItemsAsDocuments(Selection items) {
- if (items == null || items.size() == 0) {
- return new ArrayList<>(0);
- }
-
- final List<DocumentInfo> docs = new ArrayList<>(items.size());
- final int size = items.size();
- for (int i = 0; i < size; i++) {
- final Cursor cursor = mAdapter.getItem(items.get(i));
- checkNotNull(cursor, "Cursor cannot be null.");
- final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
- docs.add(doc);
- }
- return docs;
- }
-
private void copyFromClipboard() {
new AsyncTask<Void, Void, List<DocumentInfo>>() {
@@ -1426,7 +1388,7 @@
}
void copySelectedToClipboard() {
- Selection sel = mSelectionManager.getSelection(new Selection());
+ Selection sel = mModel.getSelection(new Selection());
copySelectionToClipboard(sel);
}
@@ -1458,7 +1420,7 @@
* @return true if the list of files can be copied to destination.
*/
boolean canCopy(List<DocumentInfo> files, DocumentInfo dest) {
- BaseActivity activity = (BaseActivity)getActivity();
+ BaseActivity activity = (BaseActivity) getActivity();
final RootInfo root = activity.getCurrentRoot();
@@ -1475,7 +1437,7 @@
}
void selectAllFiles() {
- boolean changed = mSelectionManager.setItemsSelected(0, mAdapter.getItemCount(), true);
+ boolean changed = mModel.selectAll();
if (changed) {
updateDisplayState();
}
@@ -1518,10 +1480,10 @@
return true;
case DragEvent.ACTION_DROP:
- int dstPosition = mRecView.getChildAdapterPosition(v);
+ int dstPosition = mRecView.getChildAdapterPosition(getContainingItemView(v));
DocumentInfo dstDir = null;
if (dstPosition != android.widget.AdapterView.INVALID_POSITION) {
- Cursor dstCursor = mAdapter.getItem(dstPosition);
+ Cursor dstCursor = mModel.getItem(dstPosition);
checkNotNull(dstCursor, "Cursor cannot be null.");
dstDir = DocumentInfo.fromDirectoryCursor(dstCursor);
// TODO: Do not drop into the directory where the documents came from.
@@ -1533,6 +1495,19 @@
}
};
+ private View getContainingItemView(View view) {
+ while (true) {
+ if (view.getLayoutParams() instanceof RecyclerView.LayoutParams) {
+ return view;
+ }
+ ViewParent parent = view.getParent();
+ if (parent == null || !(parent instanceof View)) {
+ return null;
+ }
+ view = (View) parent;
+ }
+ }
+
private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
@@ -1551,21 +1526,21 @@
};
private List<DocumentInfo> getDraggableDocuments(View currentItemView) {
- int position = mRecView.getChildAdapterPosition(currentItemView);
+ int position = mRecView.getChildAdapterPosition(getContainingItemView(currentItemView));
if (position == android.widget.AdapterView.INVALID_POSITION) {
return Collections.EMPTY_LIST;
}
- final List<DocumentInfo> selectedDocs = getSelectedDocuments();
+ final List<DocumentInfo> selectedDocs = mModel.getSelectedDocuments();
if (!selectedDocs.isEmpty()) {
- if (!mSelectionManager.getSelection().contains(position)) {
+ if (!mModel.isSelected(position)) {
// There is a selection that does not include the current item, drag nothing.
return Collections.EMPTY_LIST;
}
return selectedDocs;
}
- final Cursor cursor = mAdapter.getItem(position);
+ final Cursor cursor = mModel.getItem(position);
checkNotNull(cursor, "Cursor cannot be null.");
final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
@@ -1671,12 +1646,14 @@
mShadow.setBounds(0, 0, mShadowDimension, mShadowDimension);
}
+ @Override
public void onProvideShadowMetrics(
Point shadowSize, Point shadowTouchPoint) {
shadowSize.set(mShadowDimension, mShadowDimension);
shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2);
}
+ @Override
public void onDrawShadow(Canvas canvas) {
mShadow.draw(canvas);
}
@@ -1706,7 +1683,7 @@
extends AsyncTask<Selection, Void, List<DocumentInfo>> {
@Override
protected final List<DocumentInfo> doInBackground(Selection... selected) {
- return getItemsAsDocuments(selected[0]);
+ return mModel.getDocuments(selected[0]);
}
@Override
@@ -1775,4 +1752,315 @@
@Override
public void afterActivityCreated(DirectoryFragment fragment) {}
}
+
+ /**
+ * The data model for the current loaded directory.
+ */
+ @VisibleForTesting
+ public static final class Model implements DocumentContext {
+ private MultiSelectManager mSelectionManager;
+ private Context mContext;
+ private int mCursorCount;
+ private boolean mIsLoading;
+ private SparseBooleanArray mMarkedForDeletion = new SparseBooleanArray();
+ private UpdateListener mUpdateListener;
+ @Nullable private Cursor mCursor;
+ @Nullable private String info;
+ @Nullable private String error;
+
+ Model(Context context, MultiSelectManager selectionManager) {
+ 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;
+ }
+
+ /**
+ * Selects all files in the current directory.
+ * @return true if the selection state changed for any files.
+ */
+ boolean selectAll() {
+ return mSelectionManager.setItemsSelected(0, mCursorCount, true);
+ }
+
+ /**
+ * Clones the current selection into the given Selection object.
+ * @param selection
+ * @return The selection that was passed in, for convenience.
+ */
+ Selection getSelection(Selection selection) {
+ return mSelectionManager.getSelection(selection);
+ }
+
+ /**
+ * @return The current selection (the live instance, not a copy).
+ */
+ Selection getSelection() {
+ return mSelectionManager.getSelection();
+ }
+
+ boolean isSelected(int position) {
+ return mSelectionManager.getSelection().contains(position);
+ }
+
+ void clearSelection() {
+ mSelectionManager.clearSelection();
+ }
+
+ void update(DirectoryResult result) {
+ if (DEBUG) Log.i(TAG, "Updating model with new result set.");
+
+ if (result == null) {
+ mCursor = null;
+ mCursorCount = 0;
+ info = null;
+ error = null;
+ mIsLoading = false;
+ mUpdateListener.onModelUpdate(this);
+ return;
+ }
+
+ if (result.exception != null) {
+ Log.e(TAG, "Error while loading directory contents", result.exception);
+ mUpdateListener.onModelUpdateFailed(result.exception);
+ return;
+ }
+
+ mCursor = result.cursor;
+ mCursorCount = mCursor.getCount();
+
+ final Bundle extras = mCursor.getExtras();
+ if (extras != null) {
+ info = extras.getString(DocumentsContract.EXTRA_INFO);
+ error = extras.getString(DocumentsContract.EXTRA_ERROR);
+ mIsLoading = extras.getBoolean(DocumentsContract.EXTRA_LOADING, false);
+ }
+
+ mUpdateListener.onModelUpdate(this);
+ }
+
+ int getItemCount() {
+ return mCursorCount - mMarkedForDeletion.size();
+ }
+
+ Cursor getItem(int position) {
+ // Items marked for deletion are masked out of the UI. To do this, for every marked
+ // item whose position is less than the requested item position, advance the requested
+ // position by 1.
+ final int originalPos = position;
+ final int size = mMarkedForDeletion.size();
+ for (int i = 0; i < size; ++i) {
+ // It'd be more concise, but less efficient, to iterate over positions while calling
+ // mMarkedForDeletion.get. Instead, iterate over deleted entries.
+ if (mMarkedForDeletion.keyAt(i) <= position && mMarkedForDeletion.valueAt(i)) {
+ ++position;
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Item position adjusted for deletion. Original: " + originalPos
+ + " Adjusted: " + position);
+ }
+
+ if (position >= mCursorCount) {
+ throw new IndexOutOfBoundsException("Attempt to retrieve " + position + " of " +
+ mCursorCount + " items");
+ }
+
+ mCursor.moveToPosition(position);
+ return mCursor;
+ }
+
+ private boolean isEmpty() {
+ return mCursorCount == 0;
+ }
+
+ private boolean isLoading() {
+ return mIsLoading;
+ }
+
+ private List<DocumentInfo> getSelectedDocuments() {
+ Selection sel = getSelection(new Selection());
+ return getDocuments(sel);
+ }
+
+ List<DocumentInfo> getDocuments(Selection items) {
+ final int size = (items != null) ? items.size() : 0;
+
+ final List<DocumentInfo> docs = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ final Cursor cursor = getItem(items.get(i));
+ checkNotNull(cursor, "Cursor cannot be null.");
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
+ docs.add(doc);
+ }
+ return docs;
+ }
+
+ @Override
+ public Cursor getCursor() {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new IllegalStateException("Can't call getCursor from non-main thread.");
+ }
+ return mCursor;
+ }
+
+ List<DocumentInfo> getDocumentsMarkedForDeletion() {
+ final int size = mMarkedForDeletion.size();
+ List<DocumentInfo> docs = new ArrayList<>(size);
+
+ for (int i = 0; i < size; ++i) {
+ final int position = mMarkedForDeletion.keyAt(i);
+ checkState(position < mCursorCount);
+ mCursor.moveToPosition(position);
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(mCursor);
+ docs.add(doc);
+ }
+ return docs;
+ }
+
+ /**
+ * Marks the given files for deletion. This will remove them from the UI. Clients must then
+ * call either {@link #undoDeletion()} or {@link #finalizeDeletion()} to cancel or confirm
+ * the deletion, respectively. Only one deletion operation is allowed at a time.
+ *
+ * @param selected A selection representing the files to delete.
+ */
+ void markForDeletion(Selection selected) {
+ // Only one deletion operation at a time.
+ checkState(mMarkedForDeletion.size() == 0);
+ // There should never be more to delete than what exists.
+ checkState(mCursorCount >= selected.size());
+
+ final int size = selected.size();
+ for (int i = 0; i < size; ++i) {
+ int position = selected.get(i);
+ if (DEBUG) Log.d(TAG, "Marked position " + position + " for deletion");
+ mMarkedForDeletion.append(position, true);
+ mUpdateListener.notifyItemRemoved(position);
+ }
+ }
+
+ /**
+ * Cancels an ongoing deletion operation. All files currently marked for deletion will be
+ * unmarked, and restored in the UI. See {@link #markForDeletion(Selection)}.
+ */
+ void undoDeletion() {
+ // Iterate over deleted items, temporarily marking them false in the deletion list, and
+ // re-adding them to the UI.
+ final int size = mMarkedForDeletion.size();
+ for (int i = 0; i < size; ++i) {
+ final int position = mMarkedForDeletion.keyAt(i);
+ mMarkedForDeletion.put(position, false);
+ mUpdateListener.notifyItemInserted(position);
+ }
+
+ // Then, clear the deletion list.
+ mMarkedForDeletion.clear();
+ }
+
+ /**
+ * Finalizes an ongoing deletion operation. All files currently marked for deletion will be
+ * deleted. See {@link #markForDeletion(Selection)}.
+ *
+ * @param view The view which will be used to interact with the user (e.g. surfacing
+ * snackbars) for errors, info, etc.
+ */
+ void finalizeDeletion(Runnable errorCallback) {
+ final ContentResolver resolver = mContext.getContentResolver();
+ DeleteFilesTask task = new DeleteFilesTask(resolver, errorCallback);
+ task.execute();
+ }
+
+ /**
+ * A Task which collects the DocumentInfo for documents that have been marked for deletion,
+ * and actually deletes them.
+ */
+ private class DeleteFilesTask extends AsyncTask<Void, Void, List<DocumentInfo>> {
+ private ContentResolver mResolver;
+ private Runnable mErrorCallback;
+
+ /**
+ * @param resolver A ContentResolver for performing the actual file deletions.
+ * @param errorCallback A Runnable that is executed in the event that one or more errors
+ * occured while copying files. Execution will occur on the UI thread.
+ */
+ public DeleteFilesTask(ContentResolver resolver, Runnable errorCallback) {
+ mResolver = resolver;
+ mErrorCallback = errorCallback;
+ }
+
+ @Override
+ protected List<DocumentInfo> doInBackground(Void... params) {
+ return getDocumentsMarkedForDeletion();
+ }
+
+ @Override
+ protected void onPostExecute(List<DocumentInfo> docs) {
+ boolean hadTrouble = false;
+ for (DocumentInfo doc : docs) {
+ if (!doc.isDeleteSupported()) {
+ Log.w(TAG, doc + " could not be deleted. Skipping...");
+ hadTrouble = true;
+ continue;
+ }
+
+ ContentProviderClient client = null;
+ try {
+ if (DEBUG) Log.d(TAG, "Deleting: " + doc.displayName);
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ mResolver, doc.derivedUri.getAuthority());
+ DocumentsContract.deleteDocument(client, doc.derivedUri);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to delete " + doc);
+ hadTrouble = true;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
+ }
+ }
+
+ if (hadTrouble) {
+ // TODO show which files failed? b/23720103
+ mErrorCallback.run();
+ if (DEBUG) Log.d(TAG, "Deletion task completed. Some deletions failed.");
+ } else {
+ if (DEBUG) Log.d(TAG, "Deletion task completed successfully.");
+ }
+ mMarkedForDeletion.clear();
+ }
+ }
+
+ void addUpdateListener(UpdateListener listener) {
+ checkState(mUpdateListener == null);
+ mUpdateListener = listener;
+ }
+
+ interface UpdateListener {
+ /**
+ * Called when a successful update has occurred.
+ */
+ void onModelUpdate(Model model);
+
+ /**
+ * Called when an update has been attempted but failed.
+ */
+ void onModelUpdateFailed(Exception e);
+
+ /**
+ * Called when an item has been removed from the model.
+ */
+ void notifyItemRemoved(int position);
+
+ /**
+ * Called when an item has been added to the model.
+ */
+ void notifyItemInserted(int position);
+ }
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index fdc4bb0..1de1c6a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -19,7 +19,6 @@
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;
@@ -67,7 +66,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;
@@ -90,8 +89,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) && mState.action != ACTION_BROWSE;
if (!mShowAsDialog) {
setTheme(R.style.DocumentsNonDialogTheme);
@@ -119,8 +117,6 @@
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,15 +124,11 @@
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) {
+ if (mState.action == ACTION_BROWSE) {
mDrawer.lockClosed();
if (mShowAsDialog) {
findViewById(R.id.container_roots).setVisibility(View.GONE);
@@ -168,7 +160,7 @@
// 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) {
+ if (mState.action == ACTION_BROWSE) {
final Uri rootUri = getIntent().getData();
new RestoreRootTask(rootUri).executeOnExecutor(getCurrentExecutor());
} else {
@@ -180,8 +172,8 @@
}
@Override
- State buildDefaultState() {
- State state = new State();
+ State buildState() {
+ State state = buildDefaultState();
final Intent intent = getIntent();
final String action = intent.getAction();
@@ -193,8 +185,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)) {
@@ -206,7 +196,7 @@
Intent.EXTRA_ALLOW_MULTIPLE, false);
}
- if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
+ if (state.action == ACTION_BROWSE) {
state.acceptMimes = new String[] { "*/*" };
state.allowMultiple = true;
} else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
@@ -215,12 +205,7 @@
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) {
+ if (state.action == ACTION_BROWSE) {
state.showSize = true;
} else {
state.showSize = LocalPreferences.getDisplayFileSize(this);
@@ -232,38 +217,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 +361,11 @@
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);
+ // File size is locked visible for browse because that is the action triggered by Settings,
+ // where the user is trying to find large files to clean up.
+ // TODO: instead of setting this according to the action, use a local preference, but
+ // provide a @hide extra to let callers like Settings force-enable size visibility.
+ boolean fileSizeVisible = mState.action != ACTION_BROWSE;
if (mState.action == ACTION_CREATE
|| mState.action == ACTION_OPEN_TREE
|| mState.action == ACTION_OPEN_COPY_DESTINATION) {
@@ -428,11 +387,10 @@
createDir.setVisible(false);
}
- advanced.setVisible(!(mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) &&
- !mState.forceAdvanced);
+ advanced.setVisible(mState.action != ACTION_BROWSE && !mState.forceAdvanced);
fileSize.setVisible(fileSizeVisible);
- settings.setVisible((mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE)
+ settings.setVisible(mState.action == ACTION_BROWSE
&& (root.flags & Root.FLAG_HAS_SETTINGS) != 0);
return true;
@@ -444,11 +402,6 @@
}
@Override
- public State getDisplayState() {
- return mState;
- }
-
- @Override
void onDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
@@ -523,26 +476,6 @@
} 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);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Events.java b/packages/DocumentsUI/src/com/android/documentsui/Events.java
index 025b94f..d4c3ba3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Events.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Events.java
@@ -16,8 +16,11 @@
package com.android.documentsui;
+import android.graphics.Point;
+import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.View;
/**
* Utility code for dealing with MotionEvents.
@@ -54,6 +57,20 @@
}
/**
+ * Returns true if event was triggered by a finger or stylus touch.
+ */
+ static boolean isActionDown(MotionEvent e) {
+ return e.getActionMasked() == MotionEvent.ACTION_DOWN;
+ }
+
+ /**
+ * Returns true if event was triggered by a finger or stylus touch.
+ */
+ static boolean isActionUp(MotionEvent e) {
+ return e.getActionMasked() == MotionEvent.ACTION_UP;
+ }
+
+ /**
* Returns true if the shift is pressed.
*/
boolean isShiftPressed(MotionEvent e) {
@@ -66,4 +83,106 @@
static boolean hasShiftBit(int metaState) {
return (metaState & KeyEvent.META_SHIFT_ON) != 0;
}
+
+ /**
+ * A facade over MotionEvent primarily designed to permit for unit testing
+ * of related code.
+ */
+ interface InputEvent {
+ boolean isMouseEvent();
+ boolean isPrimaryButtonPressed();
+ boolean isSecondaryButtonPressed();
+ boolean isShiftKeyDown();
+
+ /** Returns true if the action is the initial press of a mouse or touch. */
+ boolean isActionDown();
+
+ /** Returns true if the action is the final release of a mouse or touch. */
+ boolean isActionUp();
+
+ Point getOrigin();
+
+ /** Returns true if the there is an item under the finger/cursor. */
+ boolean isOverItem();
+
+ /** Returns the adapter position of the item under the finger/cursor. */
+ int getItemPosition();
+ }
+
+ static final class MotionInputEvent implements InputEvent {
+ private final MotionEvent mEvent;
+ private final RecyclerView mView;
+ private final int mPosition;
+
+ public MotionInputEvent(MotionEvent event, RecyclerView view) {
+ mEvent = event;
+ mView = view;
+ View child = mView.findChildViewUnder(mEvent.getX(), mEvent.getY());
+ mPosition = (child != null)
+ ? mView.getChildAdapterPosition(child)
+ : RecyclerView.NO_POSITION;
+ }
+
+ @Override
+ public boolean isMouseEvent() {
+ return Events.isMouseEvent(mEvent);
+ }
+
+ @Override
+ public boolean isPrimaryButtonPressed() {
+ return mEvent.isButtonPressed(MotionEvent.BUTTON_PRIMARY);
+ }
+
+ @Override
+ public boolean isSecondaryButtonPressed() {
+ return mEvent.isButtonPressed(MotionEvent.BUTTON_SECONDARY);
+ }
+
+ @Override
+ public boolean isShiftKeyDown() {
+ return Events.hasShiftBit(mEvent.getMetaState());
+ }
+
+ @Override
+ public boolean isActionDown() {
+ return mEvent.getActionMasked() == MotionEvent.ACTION_DOWN;
+ }
+
+ @Override
+ public boolean isActionUp() {
+ return mEvent.getActionMasked() == MotionEvent.ACTION_UP;
+ }
+
+ @Override
+ public Point getOrigin() {
+ return new Point((int) mEvent.getX(), (int) mEvent.getY());
+ }
+
+ @Override
+ public boolean isOverItem() {
+ return getItemPosition() != RecyclerView.NO_POSITION;
+ }
+
+ @Override
+ public int getItemPosition() {
+ return mPosition;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("MotionInputEvent {")
+ .append("isMouseEvent=").append(isMouseEvent())
+ .append(" isPrimaryButtonPressed=").append(isPrimaryButtonPressed())
+ .append(" isSecondaryButtonPressed=").append(isSecondaryButtonPressed())
+ .append(" isShiftKeyDown=").append(isShiftKeyDown())
+ .append(" isActionDown=").append(isActionDown())
+ .append(" isActionUp=").append(isActionUp())
+ .append(" getOrigin=").append(getOrigin())
+ .append(" isOverItem=").append(isOverItem())
+ .append(" getItemPosition=").append(getItemPosition())
+ .append("}")
+ .toString();
+ }
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 7c445bf..450def7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -30,7 +30,6 @@
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;
@@ -58,12 +57,11 @@
*/
public class FilesActivity extends BaseActivity {
- public static final String TAG = "StandaloneFileManagement";
+ public static final String TAG = "FilesActivity";
static final boolean DEBUG = false;
private Toolbar mToolbar;
private Spinner mToolbarStack;
- private Toolbar mRootsToolbar;
private DirectoryContainerView mDirectoryContainer;
private ItemSelectedListener mStackListener;
private BaseAdapter mStackAdapter;
@@ -82,28 +80,16 @@
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) {
@@ -127,14 +113,14 @@
}
@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.allowMultiple = true;
state.acceptMimes = new String[] { intent.getType() };
+ state.allowMultiple = true;
// These options are specific to the DocumentsActivity.
Preconditions.checkArgument(
@@ -223,8 +209,6 @@
createDir.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
createDir.setVisible(canCreateDir);
- settings.setVisible((getCurrentRoot().flags & Root.FLAG_HAS_SETTINGS) != 0);
-
pasteFromCb.setVisible(true);
pasteFromCb.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
@@ -246,11 +230,6 @@
}
@Override
- public State getDisplayState() {
- return mState;
- }
-
- @Override
void onDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
index 9959265..ec1cb1d 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 == BaseActivity.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/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
new file mode 100644
index 0000000..7401578
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
@@ -0,0 +1,244 @@
+/*
+ * 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.BaseActivity.State.ACTION_MANAGE;
+import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
+import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+
+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 DirectoryContainerView mDirectoryContainer;
+
+ 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();
+
+ 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);
+
+ 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(getCurrentExecutor());
+ } 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);
+ mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
+
+ 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/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
index e972566..5930056 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
@@ -16,11 +16,16 @@
package com.android.documentsui;
-import static com.android.documentsui.Events.isMouseEvent;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter;
@@ -36,10 +41,9 @@
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.VisibleForTesting;
+
+import com.android.documentsui.Events.InputEvent;
+import com.android.documentsui.Events.MotionInputEvent;
import java.util.ArrayList;
import java.util.Collections;
@@ -59,7 +63,6 @@
public static final int MODE_SINGLE = 1;
private static final String TAG = "MultiSelectManager";
- private static final boolean DEBUG = false;
private final Selection mSelection = new Selection();
@@ -70,9 +73,10 @@
private final List<MultiSelectManager.Callback> mCallbacks = new ArrayList<>(1);
private Adapter<?> mAdapter;
- private MultiSelectHelper mHelper;
+ private ItemFinder mHelper;
private boolean mSingleSelect;
- private BandSelectManager mBandSelectManager;
+
+ @Nullable private BandController mBandManager;
/**
* @param recyclerView
@@ -86,20 +90,26 @@
this(
recyclerView.getAdapter(),
- new RuntimeRecyclerViewHelper(recyclerView),
+ new RuntimeItemFinder(recyclerView),
mode);
- mBandSelectManager = new BandSelectManager((RuntimeRecyclerViewHelper) mHelper);
+ if (mode == MODE_MULTIPLE) {
+ mBandManager = new BandController(
+ mHelper,
+ new RuntimeBandEnvironment(recyclerView));
+ }
GestureDetector.SimpleOnGestureListener listener =
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
- return MultiSelectManager.this.onSingleTapUp(e);
+ return MultiSelectManager.this.onSingleTapUp(
+ new MotionInputEvent(e, recyclerView));
}
@Override
public void onLongPress(MotionEvent e) {
- MultiSelectManager.this.onLongPress(e);
+ MultiSelectManager.this.onLongPress(
+ new MotionInputEvent(e, recyclerView));
}
};
@@ -112,17 +122,44 @@
recyclerView.addOnItemTouchListener(
new RecyclerView.OnItemTouchListener() {
+ @Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
detector.onTouchEvent(e);
- // Only intercept the event if it was a mouse-based band selection.
- return isMouseEvent(e) && (mBandSelectManager.mIsActive ||
- e.getActionMasked() != MotionEvent.ACTION_UP);
+ if (mBandManager == null) {
+ return false;
+ }
+
+ // b/23793622 notes the fact that we *never* receiver ACTION_DOWN
+ // events in onTouchEvent. Where it not for this issue, we'd
+ // push start handling down into handleInputEvent.
+ if (mBandManager.shouldStart(e)) {
+ // endBandSelect is handled in handleInputEvent.
+ mBandManager.startBandSelect(
+ new Point((int) e.getX(), (int) e.getY()));
+ } else if (mBandManager.isActive()
+ && Events.isMouseEvent(e)
+ && Events.isActionUp(e)) {
+ // Same issue here w b/23793622. The ACTION_UP event
+ // is only evert dispatched to onTouchEvent when
+ // there is some associated motion. If a user taps
+ // mouse, but doesn't move, then band select gets
+ // started BUT not ended. Causing phantom
+ // bands to appear when the user later clicks to start
+ // band select.
+ mBandManager.handleInputEvent(
+ new MotionInputEvent(e, recyclerView));
+ }
+
+ return mBandManager.isActive();
}
+
+ @Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
- checkState(isMouseEvent(e));
- mBandSelectManager.processMotionEvent(e);
+ mBandManager.handleInputEvent(
+ new MotionInputEvent(e, recyclerView));
}
+ @Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
});
}
@@ -132,7 +169,7 @@
* @hide
*/
@VisibleForTesting
- MultiSelectManager(Adapter<?> adapter, MultiSelectHelper helper, int mode) {
+ MultiSelectManager(Adapter<?> adapter, ItemFinder helper, int mode) {
checkNotNull(adapter, "'adapter' cannot be null.");
checkNotNull(helper, "'helper' cannot be null.");
@@ -184,13 +221,13 @@
/**
* Returns a Selection object that provides a live view
- * on the current selection. Callers wishing to get
+ * on the current selection.
*
- * @see #getSelectionSnapshot() on how to get a snapshot
+ * @see #getSelection(Selection) on how to get a snapshot
* of the selection that will not reflect future changes
* to selection.
*
- * @return The current seleciton.
+ * @return The current selection.
*/
public Selection getSelection() {
return mSelection;
@@ -250,6 +287,12 @@
notifySelectionChanged();
}
+ public void handleLayoutChanged() {
+ if (mBandManager != null) {
+ mBandManager.handleLayoutChanged();
+ }
+ }
+
/**
* Clears the selection, without notifying anyone.
*/
@@ -271,71 +314,37 @@
}
}
- private void onLongPress(MotionEvent e) {
+ @VisibleForTesting
+ void onLongPress(InputEvent input) {
if (DEBUG) Log.d(TAG, "Handling long press event.");
- int position = mHelper.findEventPosition(e);
- if (position == RecyclerView.NO_POSITION) {
- if (DEBUG) Log.i(TAG, "View is null. Cannot handle tap event.");
+ if (!input.isOverItem()) {
+ if (DEBUG) Log.i(TAG, "Cannot handle tap. No adapter position available.");
}
- onLongPress(position, e.getMetaState());
+ handleAdapterEvent(input);
}
- /**
- * TODO: Roll this back into {@link #onLongPress(MotionEvent)} once MotionEvent
- * can be mocked.
- *
- * @param position
- * @param metaState as returned from {@link MotionEvent#getMetaState()}.
- * @hide
- */
@VisibleForTesting
- void onLongPress(int position, int metaState) {
- if (position == RecyclerView.NO_POSITION) {
- if (DEBUG) Log.i(TAG, "View is null. Cannot handle tap event.");
- }
-
- handlePositionChanged(position, metaState);
- }
-
- /**
- * @param e
- * @return true if the event was consumed.
- */
- private boolean onSingleTapUp(MotionEvent e) {
- if (DEBUG) Log.d(TAG, "Handling tap event.");
- return onSingleTapUp(mHelper.findEventPosition(e), e.getMetaState(), e.getToolType(0));
- }
-
- /**
- * TODO: Roll this into {@link #onSingleTapUp(MotionEvent)} once MotionEvent
- * can be mocked.
- *
- * @param position
- * @param metaState as returned from {@link MotionEvent#getMetaState()}.
- * @param toolType
- * @return true if the event was consumed.
- * @hide
- */
- @VisibleForTesting
- boolean onSingleTapUp(int position, int metaState, int toolType) {
+ boolean onSingleTapUp(InputEvent input) {
+ if (DEBUG) Log.d(TAG, "Processing tap event.");
if (mSelection.isEmpty()) {
// if this is a mouse click on an item, start selection mode.
- if (position != RecyclerView.NO_POSITION && Events.isMouseType(toolType)) {
- toggleSelection(position);
+ // TODO: && input.isPrimaryButtonPressed(), but it is returning false.
+ if (input.isOverItem() && input.isMouseEvent()) {
+ toggleSelection(input.getItemPosition());
}
return false;
}
- if (position == RecyclerView.NO_POSITION) {
- if (DEBUG) Log.d(TAG, "View is null. Canceling selection.");
+ if (!input.isOverItem()) {
+ if (DEBUG) Log.d(TAG, "Activity has no position. Canceling selection.");
clearSelection();
return false;
}
- handlePositionChanged(position, metaState);
- return false;
+ handleAdapterEvent(input);
+ return true;
}
/**
@@ -343,15 +352,15 @@
* held down, this performs a range select; otherwise, it simply toggles the item's selection
* state.
*/
- private void handlePositionChanged(int position, int metaState) {
- if (Events.hasShiftBit(metaState) && mRanger != null) {
- mRanger.snapSelection(position);
+ private void handleAdapterEvent(InputEvent input) {
+ if (mRanger != null && input.isShiftKeyDown()) {
+ mRanger.snapSelection(input.getItemPosition());
// We're being lazy here notifying even when something might not have changed.
// To make this more correct, we'd need to update the Ranger class to return
// information about what has changed.
notifySelectionChanged();
- } else if (toggleSelection(position)) {
+ } else if (toggleSelection(input.getItemPosition())) {
notifySelectionChanged();
}
}
@@ -723,16 +732,6 @@
mTotalSelection = mSavedSelection.clone();
}
- private boolean flip(int position) {
- if (contains(position)) {
- remove(position);
- return false;
- } else {
- add(position);
- return true;
- }
- }
-
/** @hide */
@VisibleForTesting
boolean add(int position) {
@@ -866,109 +865,114 @@
}
/**
- * Provides functionality for MultiSelectManager. In practice, use RuntimeRecyclerViewHelper;
- * this interface exists only for mocking in tests.
+ * Provides functionality for MultiSelectManager. Exists primarily to tests that are
+ * fully isolated from RecyclerView.
*/
- interface MultiSelectHelper {
- int findEventPosition(MotionEvent e);
+ interface ItemFinder {
+ int findItemPosition(MotionEvent e);
+ }
+
+ /** ItemFinder implementation backed by good ol' RecyclerView. */
+ private static final class RuntimeItemFinder implements ItemFinder {
+
+ private final RecyclerView mView;
+
+ RuntimeItemFinder(RecyclerView view) {
+ mView = view;
+ }
+
+ @Override
+ public int findItemPosition(MotionEvent e) {
+ View view = mView.findChildViewUnder(e.getX(), e.getY());
+ return view != null
+ ? mView.getChildAdapterPosition(view)
+ : RecyclerView.NO_POSITION;
+ }
}
/**
- * Provides functionality for BandSelectManager. In practice, use RuntimeRecyclerViewHelper;
- * this interface exists only for mocking in tests.
+ * Provides functionality for BandController. Exists primarily to tests that are
+ * fully isolated from RecyclerView.
*/
- interface BandManagerHelper {
- void drawBand(Rect rect);
- void addOnScrollListener(RecyclerView.OnScrollListener listener);
- int findEventPosition(MotionEvent e);
- int getHeight();
+ interface BandEnvironment {
+ void showBand(Rect rect);
void hideBand();
- void invalidateView();
- void postRunnable(Runnable r);
- void removeCallback(Runnable r);
- void scrollBy(int dy);
- }
-
- /**
- * Provides functionality for BandSelectModel. In practice, use RuntimeRecyclerViewHelper;
- * this interface exists only for mocking in tests.
- */
- interface BandModelHelper {
void addOnScrollListener(RecyclerView.OnScrollListener listener);
+ void removeOnScrollListener(RecyclerView.OnScrollListener listener);
+ void scrollBy(int dy);
+ int getHeight();
+ void invalidateView();
+ void runAtNextFrame(Runnable r);
+ void removeCallback(Runnable r);
Point createAbsolutePoint(Point relativePoint);
Rect getAbsoluteRectForChildViewAt(int index);
int getAdapterPositionAt(int index);
- int getNumColumns();
- int getNumRows();
- int getTotalChildCount();
+ int getColumnCount();
+ int getRowCount();
+ int getChildCount();
int getVisibleChildCount();
- void removeOnScrollListener(RecyclerView.OnScrollListener listener);
}
- /**
- * Concrete RecyclerViewHelper implementation for use within the Files app.
- */
- private static final class RuntimeRecyclerViewHelper implements MultiSelectHelper,
- BandManagerHelper, BandModelHelper {
+ /** RvFacade implementation backed by good ol' RecyclerView. */
+ private static final class RuntimeBandEnvironment implements BandEnvironment {
- private final RecyclerView mRecyclerView;
- private final Drawable mBandSelectRect;
+ private final RecyclerView mView;
+ private final Drawable mBand;
private boolean mIsOverlayShown = false;
- RuntimeRecyclerViewHelper(RecyclerView rv) {
- mRecyclerView = rv;
- mBandSelectRect = mRecyclerView.getContext().getTheme().getDrawable(
- R.drawable.band_select_overlay);
+ RuntimeBandEnvironment(RecyclerView rv) {
+ mView = rv;
+ mBand = mView.getContext().getTheme().getDrawable(R.drawable.band_select_overlay);
}
@Override
public int getAdapterPositionAt(int index) {
- View child = mRecyclerView.getChildAt(index);
- return mRecyclerView.getChildViewHolder(child).getAdapterPosition();
+ View child = mView.getChildAt(index);
+ return mView.getChildViewHolder(child).getAdapterPosition();
}
@Override
public void addOnScrollListener(OnScrollListener listener) {
- mRecyclerView.addOnScrollListener(listener);
+ mView.addOnScrollListener(listener);
}
@Override
public void removeOnScrollListener(OnScrollListener listener) {
- mRecyclerView.removeOnScrollListener(listener);
+ mView.removeOnScrollListener(listener);
}
@Override
public Point createAbsolutePoint(Point relativePoint) {
- return new Point(relativePoint.x + mRecyclerView.computeHorizontalScrollOffset(),
- relativePoint.y + mRecyclerView.computeVerticalScrollOffset());
+ return new Point(relativePoint.x + mView.computeHorizontalScrollOffset(),
+ relativePoint.y + mView.computeVerticalScrollOffset());
}
@Override
public Rect getAbsoluteRectForChildViewAt(int index) {
- final View child = mRecyclerView.getChildAt(index);
+ final View child = mView.getChildAt(index);
final Rect childRect = new Rect();
child.getHitRect(childRect);
- childRect.left += mRecyclerView.computeHorizontalScrollOffset();
- childRect.right += mRecyclerView.computeHorizontalScrollOffset();
- childRect.top += mRecyclerView.computeVerticalScrollOffset();
- childRect.bottom += mRecyclerView.computeVerticalScrollOffset();
+ childRect.left += mView.computeHorizontalScrollOffset();
+ childRect.right += mView.computeHorizontalScrollOffset();
+ childRect.top += mView.computeVerticalScrollOffset();
+ childRect.bottom += mView.computeVerticalScrollOffset();
return childRect;
}
@Override
+ public int getChildCount() {
+ return mView.getAdapter().getItemCount();
+ }
+
+ @Override
public int getVisibleChildCount() {
- return mRecyclerView.getChildCount();
+ return mView.getChildCount();
}
@Override
- public int getTotalChildCount() {
- return mRecyclerView.getAdapter().getItemCount();
- }
-
- @Override
- public int getNumColumns() {
- LayoutManager layoutManager = mRecyclerView.getLayoutManager();
+ public int getColumnCount() {
+ LayoutManager layoutManager = mView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
return ((GridLayoutManager) layoutManager).getSpanCount();
}
@@ -978,57 +982,49 @@
}
@Override
- public int getNumRows() {
- int numFullColumns = getTotalChildCount() / getNumColumns();
- boolean hasPartiallyFullColumn = getTotalChildCount() % getNumColumns() != 0;
+ public int getRowCount() {
+ int numFullColumns = getChildCount() / getColumnCount();
+ boolean hasPartiallyFullColumn = getChildCount() % getColumnCount() != 0;
return numFullColumns + (hasPartiallyFullColumn ? 1 : 0);
}
@Override
- public int findEventPosition(MotionEvent e) {
- View view = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
- return view != null
- ? mRecyclerView.getChildAdapterPosition(view)
- : RecyclerView.NO_POSITION;
- }
-
- @Override
public int getHeight() {
- return mRecyclerView.getHeight();
+ return mView.getHeight();
}
@Override
public void invalidateView() {
- mRecyclerView.invalidate();
+ mView.invalidate();
}
@Override
- public void postRunnable(Runnable r) {
- mRecyclerView.postOnAnimation(r);
+ public void runAtNextFrame(Runnable r) {
+ mView.postOnAnimation(r);
}
@Override
public void removeCallback(Runnable r) {
- mRecyclerView.removeCallbacks(r);
+ mView.removeCallbacks(r);
}
@Override
public void scrollBy(int dy) {
- mRecyclerView.scrollBy(0, dy);
+ mView.scrollBy(0, dy);
}
@Override
- public void drawBand(Rect rect) {
- mBandSelectRect.setBounds(rect);
+ public void showBand(Rect rect) {
+ mBand.setBounds(rect);
if (!mIsOverlayShown) {
- mRecyclerView.getOverlay().add(mBandSelectRect);
+ mView.getOverlay().add(mBand);
}
}
@Override
public void hideBand() {
- mRecyclerView.getOverlay().remove(mBandSelectRect);
+ mView.getOverlay().remove(mBand);
}
}
@@ -1165,58 +1161,91 @@
* and {@link MultiSelectManager}. This class is responsible for rendering the band select
* overlay and selecting overlaid items via MultiSelectManager.
*/
- public class BandSelectManager extends RecyclerView.OnScrollListener
- implements BandSelectModel.OnSelectionChangedListener {
+ public class BandController extends RecyclerView.OnScrollListener
+ implements GridModel.OnSelectionChangedListener {
private static final int NOT_SET = -1;
- private final BandManagerHelper mHelper;
+ private final ItemFinder mItemFinder;
+ private final BandEnvironment mEnvironment;
+ private final Runnable mModelBuilder;
- private boolean mIsActive;
- private Point mOrigin;
- private Point mPointer;
- private Rect mBounds;
- private BandSelectModel mModel;
+ @Nullable private Rect mBounds;
+ @Nullable private Point mCurrentPosition;
+ @Nullable private Point mOrigin;
+ @Nullable private GridModel mModel;
// The time at which the current band selection-induced scroll began. If no scroll is in
// progress, the value is NOT_SET.
private long mScrollStartTime = NOT_SET;
private final Runnable mViewScroller = new ViewScroller();
- public <T extends BandManagerHelper & BandModelHelper>
- BandSelectManager(T helper) {
- mHelper = helper;
- mHelper.addOnScrollListener(this);
- mModel = new BandSelectModel(helper);
- mModel.addOnSelectionChangedListener(this);
+ public BandController(ItemFinder finder, final BandEnvironment environment) {
+ mItemFinder = finder;
+ mEnvironment = environment;
+ mEnvironment.addOnScrollListener(this);
+
+ mModelBuilder = new Runnable() {
+ @Override
+ public void run() {
+ mModel = new GridModel(environment);
+ mModel.addOnSelectionChangedListener(BandController.this);
+ }
+ };
+ }
+
+ private boolean isActive() {
+ return mModel != null;
+ }
+
+ /**
+ * Handle a change in layout by cleaning up and getting rid of the old model and creating
+ * a new model which will track the new layout.
+ */
+ public void handleLayoutChanged() {
+ if (mModel != null) {
+ mModel.removeOnSelectionChangedListener(this);
+ mModel.stopListening();
+
+ // build a new model, all fresh and happy.
+ mModelBuilder.run();
+ }
+ }
+
+ boolean shouldStart(MotionEvent e) {
+ return !isActive()
+ && Events.isMouseEvent(e) // a mouse
+ && Events.isActionDown(e) // the initial button press
+ && mAdapter.getItemCount() > 0
+ && mItemFinder.findItemPosition(e) == RecyclerView.NO_ID; // in empty space
+ }
+
+ boolean shouldStop(InputEvent input) {
+ return isActive()
+ && input.isMouseEvent()
+ && input.isActionUp();
}
/**
* Processes a MotionEvent by starting, ending, or resizing the band select overlay.
- * @param e
+ * @param input
*/
- private void processMotionEvent(MotionEvent e) {
- if (!isMouseEvent(e)) {
- return;
- }
+ private void handleInputEvent(InputEvent input) {
+ checkArgument(input.isMouseEvent());
- if (mIsActive && e.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (shouldStop(input)) {
endBandSelect();
return;
}
- mPointer = new Point((int) e.getX(), (int) e.getY());
- if (!mIsActive) {
- // Only start a band select if the pointer is in margins between items, not
- // actually within an item's bounds.
- if (mHelper.findEventPosition(e) != RecyclerView.NO_POSITION) {
- return;
- }
- startBandSelect();
- } else {
- mModel.resizeSelection(mPointer);
+ // We shouldn't get any events in this method when band select is not active,
+ // but it turns some guests show up late to the party.
+ if (!isActive()) {
+ return;
}
+ mCurrentPosition = input.getOrigin();
+ mModel.resizeSelection(input.getOrigin());
scrollViewIfNecessary();
resizeBandSelectRectangle();
}
@@ -1224,12 +1253,11 @@
/**
* Starts band select by adding the drawable to the RecyclerView's overlay.
*/
- private void startBandSelect() {
- if (DEBUG) {
- Log.d(TAG, "Starting band select from (" + mPointer.x + "," + mPointer.y + ").");
- }
- mIsActive = true;
- mOrigin = new Point(mPointer.x, mPointer.y);
+ private void startBandSelect(Point origin) {
+ if (DEBUG) Log.d(TAG, "Starting band select @ " + origin);
+
+ mOrigin = origin;
+ mModelBuilder.run(); // Creates a new selection model.
mModel.startSelection(mOrigin);
}
@@ -1237,9 +1265,9 @@
* Scrolls the view if necessary.
*/
private void scrollViewIfNecessary() {
- mHelper.removeCallback(mViewScroller);
+ mEnvironment.removeCallback(mViewScroller);
mViewScroller.run();
- mHelper.invalidateView();
+ mEnvironment.invalidateView();
}
/**
@@ -1247,11 +1275,11 @@
* two opposite corners of the selection.
*/
private void resizeBandSelectRectangle() {
- mBounds = new Rect(Math.min(mOrigin.x, mPointer.x),
- Math.min(mOrigin.y, mPointer.y),
- Math.max(mOrigin.x, mPointer.x),
- Math.max(mOrigin.y, mPointer.y));
- mHelper.drawBand(mBounds);
+ mBounds = new Rect(Math.min(mOrigin.x, mCurrentPosition.x),
+ Math.min(mOrigin.y, mCurrentPosition.y),
+ Math.max(mOrigin.x, mCurrentPosition.x),
+ Math.max(mOrigin.y, mCurrentPosition.y));
+ mEnvironment.showBand(mBounds);
}
/**
@@ -1259,14 +1287,20 @@
*/
private void endBandSelect() {
if (DEBUG) Log.d(TAG, "Ending band select.");
- mIsActive = false;
- mHelper.hideBand();
+
+ mEnvironment.hideBand();
mSelection.applyProvisionalSelection();
mModel.endSelection();
int firstSelected = mModel.getPositionNearestOrigin();
- if (firstSelected != BandSelectModel.NOT_SET) {
+ if (!mSelection.contains(firstSelected)) {
+ Log.w(TAG, "First selected by band is NOT in selection!");
+ // Sadly this is really happening. Need to figure out what's going on.
+ } else if (firstSelected != GridModel.NOT_SET) {
setSelectionFocusBegin(firstSelected);
}
+
+ mModel = null;
+ mOrigin = null;
}
@Override
@@ -1295,13 +1329,13 @@
// that one additional pixel is added here so that the view still scrolls when the
// pointer is exactly at the top or bottom.
int pixelsPastView = 0;
- if (mPointer.y <= 0) {
- pixelsPastView = mPointer.y - 1;
- } else if (mPointer.y >= mHelper.getHeight() - 1) {
- pixelsPastView = mPointer.y - mHelper.getHeight() + 1;
+ if (mCurrentPosition.y <= 0) {
+ pixelsPastView = mCurrentPosition.y - 1;
+ } else if (mCurrentPosition.y >= mEnvironment.getHeight() - 1) {
+ pixelsPastView = mCurrentPosition.y - mEnvironment.getHeight() + 1;
}
- if (!mIsActive || pixelsPastView == 0) {
+ if (!isActive() || pixelsPastView == 0) {
// If band selection is inactive, or if it is active but not at the edge of the
// view, no scrolling is necessary.
mScrollStartTime = NOT_SET;
@@ -1317,10 +1351,10 @@
// Compute the number of pixels to scroll, and scroll that many pixels.
final int numPixels = computeScrollDistance(
pixelsPastView, System.currentTimeMillis() - mScrollStartTime);
- mHelper.scrollBy(numPixels);
+ mEnvironment.scrollBy(numPixels);
- mHelper.removeCallback(mViewScroller);
- mHelper.postRunnable(this);
+ mEnvironment.removeCallback(mViewScroller);
+ mEnvironment.runAtNextFrame(this);
}
/**
@@ -1333,14 +1367,14 @@
* @return
*/
private int computeScrollDistance(int pixelsPastView, long scrollDuration) {
- final int maxScrollStep = mHelper.getHeight();
+ final int maxScrollStep = mEnvironment.getHeight();
final int direction = (int) Math.signum(pixelsPastView);
final int absPastView = Math.abs(pixelsPastView);
// Calculate the ratio of how far out of the view the pointer currently resides to
// the entire height of the view.
final float outOfBoundsRatio = Math.min(
- 1.0f, (float) absPastView / mHelper.getHeight());
+ 1.0f, (float) absPastView / mEnvironment.getHeight());
// Interpolate this ratio and use it to compute the maximum scroll that should be
// possible for this step.
final float cappedScrollStep =
@@ -1387,7 +1421,7 @@
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- if (!mIsActive) {
+ if (!isActive()) {
return;
}
@@ -1403,7 +1437,7 @@
* RecyclerView to determine where its items are placed; then, once band selection is underway,
* it alerts listeners of which items are covered by the selections.
*/
- public static final class BandSelectModel extends RecyclerView.OnScrollListener {
+ public static final class GridModel extends RecyclerView.OnScrollListener {
public static final int NOT_SET = -1;
@@ -1417,8 +1451,9 @@
private static final int LOWER_LEFT = LOWER | LEFT;
private static final int LOWER_RIGHT = LOWER | RIGHT;
- private final BandModelHelper mHelper;
- private final List<OnSelectionChangedListener> mOnSelectionChangedListeners = new ArrayList<>();
+ private final BandEnvironment mHelper;
+ private final List<OnSelectionChangedListener> mOnSelectionChangedListeners =
+ new ArrayList<>();
// Map from the x-value of the left side of a SparseBooleanArray of adapter positions, keyed
// by their y-offset. For example, if the first column of the view starts at an x-value of 5,
@@ -1426,15 +1461,13 @@
// value for key y is the adapter position for the item whose y-offset is y.
private final SparseArray<SparseIntArray> mColumns = new SparseArray<>();
- // List of limits along the x-axis. For example, if the view has two columns, this list will
- // have two elements, each of which lists the lower- and upper-limits of the x-values of the
- // view items. This list is sorted from furthest left to furthest right.
- private final List<Limits> mXLimitsList = new ArrayList<>();
+ // List of limits along the x-axis (columns).
+ // This list is sorted from furthest left to furthest right.
+ private final List<Limits> mColumnBounds = new ArrayList<>();
- // Like mXLimitsList, but for y-coordinates. Note that this list only contains items which
- // have been in the viewport. Thus, limits which exist in an area of the view to which the
- // view has not scrolled are not present in the list.
- private final List<Limits> mYLimitsList = new ArrayList<>();
+ // List of limits along the y-axis (rows). Note that this list only contains items which
+ // have been in the viewport.
+ private final List<Limits> mRowBounds = new ArrayList<>();
// The adapter positions which have been recorded so far.
private final SparseBooleanArray mKnownPositions = new SparseBooleanArray();
@@ -1456,7 +1489,7 @@
// should expand from when Shift+click is used.
private int mPositionNearestOrigin = NOT_SET;
- BandSelectModel(BandModelHelper helper) {
+ GridModel(BandEnvironment helper) {
mHelper = helper;
mHelper.addOnScrollListener(this);
}
@@ -1547,16 +1580,16 @@
* @param adapterPosition The position of the child view being processed.
*/
private void recordItemData(Rect absoluteChildRect, int adapterPosition) {
- if (mXLimitsList.size() != mHelper.getNumColumns()) {
+ if (mColumnBounds.size() != mHelper.getColumnCount()) {
// If not all x-limits have been recorded, record this one.
recordLimits(
- mXLimitsList, new Limits(absoluteChildRect.left, absoluteChildRect.right));
+ mColumnBounds, new Limits(absoluteChildRect.left, absoluteChildRect.right));
}
- if (mYLimitsList.size() != mHelper.getNumRows()) {
+ if (mRowBounds.size() != mHelper.getRowCount()) {
// If not all y-limits have been recorded, record this one.
recordLimits(
- mYLimitsList, new Limits(absoluteChildRect.top, absoluteChildRect.bottom));
+ mRowBounds, new Limits(absoluteChildRect.top, absoluteChildRect.bottom));
}
SparseIntArray columnList = mColumns.get(absoluteChildRect.left);
@@ -1597,7 +1630,7 @@
* Computes the currently-selected items.
*/
private void computeCurrentSelection() {
- if (areItemsCoveredBySelection(mRelativePointer, mRelativeOrigin)) {
+ if (areItemsCoveredByBand(mRelativePointer, mRelativeOrigin)) {
updateSelection(computeBounds());
} else {
mSelection.clear();
@@ -1621,17 +1654,17 @@
*/
private void updateSelection(Rect rect) {
int columnStartIndex =
- Collections.binarySearch(mXLimitsList, new Limits(rect.left, rect.left));
+ Collections.binarySearch(mColumnBounds, new Limits(rect.left, rect.left));
checkState(columnStartIndex >= 0);
int columnEndIndex = columnStartIndex;
- for (int i = columnStartIndex;
- i < mXLimitsList.size() && mXLimitsList.get(i).lowerLimit <= rect.right; i++) {
+ for (int i = columnStartIndex; i < mColumnBounds.size()
+ && mColumnBounds.get(i).lowerLimit <= rect.right; i++) {
columnEndIndex = i;
}
SparseIntArray firstColumn =
- mColumns.get(mXLimitsList.get(columnStartIndex).lowerLimit);
+ mColumns.get(mColumnBounds.get(columnStartIndex).lowerLimit);
int rowStartIndex = firstColumn.indexOfKey(rect.top);
if (rowStartIndex < 0) {
mPositionNearestOrigin = NOT_SET;
@@ -1655,7 +1688,7 @@
int columnStartIndex, int columnEndIndex, int rowStartIndex, int rowEndIndex) {
mSelection.clear();
for (int column = columnStartIndex; column <= columnEndIndex; column++) {
- SparseIntArray items = mColumns.get(mXLimitsList.get(column).lowerLimit);
+ SparseIntArray items = mColumns.get(mColumnBounds.get(column).lowerLimit);
for (int row = rowStartIndex; row <= rowEndIndex; row++) {
int position = items.get(items.keyAt(row));
mSelection.append(position, true);
@@ -1717,7 +1750,7 @@
* of item columns and the top- and bottom sides of item rows so that it can be determined
* whether the pointer is located within the bounds of an item.
*/
- private class Limits implements Comparable<Limits> {
+ private static class Limits implements Comparable<Limits> {
int lowerLimit;
int upperLimit;
@@ -1755,7 +1788,7 @@
* selection of items within those Limits as opposed to a search through every item to see if a
* given coordinate value falls within those Limits.
*/
- private class RelativeCoordinate
+ private static class RelativeCoordinate
implements Comparable<RelativeCoordinate> {
/**
* Location describing points after the last known item.
@@ -1806,8 +1839,7 @@
* @param value The coordinate value.
*/
RelativeCoordinate(List<Limits> limitsList, int value) {
- Limits dummyLimits = new Limits(value, value);
- int index = Collections.binarySearch(limitsList, dummyLimits);
+ int index = Collections.binarySearch(limitsList, new Limits(value, value));
if (index >= 0) {
this.type = WITHIN_LIMITS;
@@ -1874,8 +1906,8 @@
final RelativeCoordinate yLocation;
RelativePoint(Point point) {
- this.xLocation = new RelativeCoordinate(mXLimitsList, point.x);
- this.yLocation = new RelativeCoordinate(mYLimitsList, point.y);
+ this.xLocation = new RelativeCoordinate(mColumnBounds, point.x);
+ this.yLocation = new RelativeCoordinate(mRowBounds, point.y);
}
@Override
@@ -1897,19 +1929,19 @@
Rect rect = new Rect();
rect.left = getCoordinateValue(
min(mRelativeOrigin.xLocation, mRelativePointer.xLocation),
- mXLimitsList,
+ mColumnBounds,
true);
rect.right = getCoordinateValue(
max(mRelativeOrigin.xLocation, mRelativePointer.xLocation),
- mXLimitsList,
+ mColumnBounds,
false);
rect.top = getCoordinateValue(
min(mRelativeOrigin.yLocation, mRelativePointer.yLocation),
- mYLimitsList,
+ mRowBounds,
true);
rect.bottom = getCoordinateValue(
max(mRelativeOrigin.yLocation, mRelativePointer.yLocation),
- mYLimitsList,
+ mRowBounds,
false);
return rect;
}
@@ -1970,7 +2002,7 @@
throw new RuntimeException("Invalid coordinate value.");
}
- private boolean areItemsCoveredBySelection(
+ private boolean areItemsCoveredByBand(
RelativePoint first, RelativePoint second) {
return doesCoordinateLocationCoverItems(first.xLocation, second.xLocation) &&
doesCoordinateLocationCoverItems(first.yLocation, second.yLocation);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index b414ee3..0c1ebc1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -16,9 +16,19 @@
package com.android.documentsui;
+import android.content.Context;
+
/**
* @hide
*/
public final class Shared {
+ public static final boolean DEBUG = false;
public static final String TAG = "Documents";
+
+ /**
+ * Generates a formatted quantity string.
+ */
+ public static final String getQuantityString(Context context, int resourceId, int quantity) {
+ return context.getResources().getQuantityString(resourceId, quantity, quantity);
+ }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
new file mode 100644
index 0000000..5505f35
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.DocumentsContract.Document;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.test.mock.MockContentResolver;
+
+import com.android.documentsui.DirectoryFragment.Model;
+import com.android.documentsui.MultiSelectManager.Selection;
+import com.android.documentsui.model.DocumentInfo;
+
+import java.util.List;
+
+
+public class DirectoryFragmentModelTest extends AndroidTestCase {
+
+ private static final int ITEM_COUNT = 5;
+ private static final String[] COLUMNS = new String[]{
+ Document.COLUMN_DOCUMENT_ID
+ };
+ private static Cursor cursor;
+
+ private Context mContext;
+ private Model model;
+
+ public void setUp() {
+ setupTestContext();
+
+ MatrixCursor c = new MatrixCursor(COLUMNS);
+ for (int i = 0; i < ITEM_COUNT; ++i) {
+ MatrixCursor.RowBuilder row = c.newRow();
+ row.add(COLUMNS[0], i);
+ }
+ cursor = c;
+
+ DirectoryResult r = new DirectoryResult();
+ r.cursor = cursor;
+
+ model = new Model(mContext, null);
+ model.addUpdateListener(new DummyListener());
+ model.update(r);
+ }
+
+ // Tests that the item count is correct.
+ public void testItemCount() {
+ assertEquals(ITEM_COUNT, model.getItemCount());
+ }
+
+ // Tests that the item count is correct after a deletion.
+ public void testItemCount_WithDeletion() {
+ // Simulate deleting 2 files.
+ delete(2, 4);
+
+ assertEquals(ITEM_COUNT - 2, model.getItemCount());
+
+ // Finalize the deletion. Provide a callback that just ignores errors.
+ model.finalizeDeletion(
+ new Runnable() {
+ @Override
+ public void run() {}
+ });
+ assertEquals(ITEM_COUNT - 2, model.getItemCount());
+ }
+
+ // Tests that the item count is correct after a deletion is undone.
+ public void testItemCount_WithUndoneDeletion() {
+ // Simulate deleting 2 files.
+ delete(0, 3);
+
+ // Undo the deletion
+ model.undoDeletion();
+ assertEquals(ITEM_COUNT, model.getItemCount());
+
+ }
+
+ // Tests that the right things are marked for deletion.
+ public void testMarkForDeletion() {
+ delete(1, 3);
+
+ List<DocumentInfo> docs = model.getDocumentsMarkedForDeletion();
+ assertEquals(2, docs.size());
+ assertEquals("1", docs.get(0).documentId);
+ assertEquals("3", docs.get(1).documentId);
+ }
+
+ // Tests the base case for Model.getItem.
+ public void testGetItem() {
+ for (int i = 0; i < ITEM_COUNT; ++i) {
+ Cursor c = model.getItem(i);
+ assertEquals(i, c.getPosition());
+ }
+ }
+
+ // Tests that Model.getItem returns the right items after a deletion.
+ public void testGetItem_WithDeletion() {
+ // Simulate deleting 2 files.
+ delete(2, 3);
+
+ List<DocumentInfo> docs = getDocumentInfo(0, 1, 2);
+ assertEquals("0", docs.get(0).documentId);
+ assertEquals("1", docs.get(1).documentId);
+ assertEquals("4", docs.get(2).documentId);
+ }
+
+ // Tests that Model.getItem returns the right items after a deletion is undone.
+ public void testGetItem_WithCancelledDeletion() {
+ delete(0, 1);
+ model.undoDeletion();
+
+ // Test that all documents are accounted for, in the right position.
+ for (int i = 0; i < ITEM_COUNT; ++i) {
+ assertEquals(Integer.toString(i), getDocumentInfo(i).get(0).documentId);
+ }
+ }
+
+ private void setupTestContext() {
+ final MockContentResolver resolver = new MockContentResolver();
+ mContext = new ContextWrapper(getContext()) {
+ @Override
+ public ContentResolver getContentResolver() {
+ return resolver;
+ }
+ };
+ }
+
+ private void delete(int... items) {
+ Selection sel = new Selection();
+ for (int item: items) {
+ sel.add(item);
+ }
+ model.markForDeletion(sel);
+ }
+
+ private List<DocumentInfo> getDocumentInfo(int... items) {
+ Selection sel = new Selection();
+ for (int item: items) {
+ sel.add(item);
+ }
+ return model.getDocuments(sel);
+ }
+
+ private static class DummyListener implements Model.UpdateListener {
+ public void onModelUpdate(Model model) {}
+ public void onModelUpdateFailed(Exception e) {}
+ public void notifyItemRemoved(int position) {}
+ public void notifyItemInserted(int position) {}
+ }
+
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
index b82251c..25d4ed4 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
@@ -16,11 +16,11 @@
package com.android.documentsui;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.support.v7.widget.RecyclerView;
import android.util.SparseBooleanArray;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -52,7 +52,6 @@
private MultiSelectManager mManager;
private TestAdapter mAdapter;
private TestCallback mCallback;
-
private EventHelper mEventHelper;
@Before
@@ -72,14 +71,14 @@
@Test
public void mouseClick_ShiftClickExtendsSelection() {
- click(7);
+ longPress(7);
shiftClick(11);
assertRangeSelection(7, 11);
}
@Test
public void mouseClick_NoPosition_ClearsSelection() {
- mManager.onLongPress(7, 0);
+ longPress(7);
click(11);
click(RecyclerView.NO_POSITION);
assertSelection();
@@ -95,27 +94,27 @@
@Test
public void longPress_StartsSelectionMode() {
- mManager.onLongPress(7, 0);
+ longPress(7);
assertSelection(7);
}
@Test
public void longPress_SecondPressExtendsSelection() {
- mManager.onLongPress(7, 0);
- mManager.onLongPress(99, 0);
+ longPress(7);
+ longPress(99);
assertSelection(7, 99);
}
@Test
public void singleTapUp_UnselectsSelectedItem() {
- mManager.onLongPress(7, 0);
+ longPress(7);
tap(7);
assertSelection();
}
@Test
public void singleTapUp_NoPosition_ClearsSelection() {
- mManager.onLongPress(7, 0);
+ longPress(7);
tap(11);
tap(RecyclerView.NO_POSITION);
assertSelection();
@@ -123,7 +122,7 @@
@Test
public void singleTapUp_ExtendsSelection() {
- mManager.onLongPress(99, 0);
+ longPress(99);
tap(7);
tap(13);
tap(129899);
@@ -132,21 +131,21 @@
@Test
public void singleTapUp_ShiftCreatesRangeSelection() {
- mManager.onLongPress(7, 0);
+ longPress(7);
shiftTap(17);
assertRangeSelection(7, 17);
}
@Test
public void singleTapUp_ShiftCreatesRangeSeletion_Backwards() {
- mManager.onLongPress(17, 0);
+ longPress(17);
shiftTap(7);
assertRangeSelection(7, 17);
}
@Test
public void singleTapUp_SecondShiftClickExtendsSelection() {
- mManager.onLongPress(7, 0);
+ longPress(7);
shiftTap(11);
shiftTap(17);
assertRangeSelection(7, 17);
@@ -154,7 +153,7 @@
@Test
public void singleTapUp_MultipleContiguousRangesSelected() {
- mManager.onLongPress(7, 0);
+ longPress(7);
shiftTap(11);
tap(20);
shiftTap(25);
@@ -165,7 +164,7 @@
@Test
public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() {
- mManager.onLongPress(7, 0);
+ longPress(7);
shiftTap(17);
shiftTap(10);
assertRangeSelection(7, 10);
@@ -173,7 +172,7 @@
@Test
public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() {
- mManager.onLongPress(17, 0);
+ mManager.onLongPress(TestInputEvent.tap(17));
shiftTap(7);
shiftTap(14);
assertRangeSelection(14, 17);
@@ -182,7 +181,7 @@
@Test
public void singleTapUp_ShiftReversesSelectionDirection() {
- mManager.onLongPress(7, 0);
+ longPress(7);
shiftTap(17);
shiftTap(0);
assertRangeSelection(0, 7);
@@ -192,7 +191,7 @@
public void singleSelectMode() {
mManager = new MultiSelectManager(mAdapter, mEventHelper, MultiSelectManager.MODE_SINGLE);
mManager.addCallback(mCallback);
- tap(20);
+ longPress(20);
tap(13);
assertSelection(13);
}
@@ -201,7 +200,7 @@
public void singleSelectMode_ShiftTap() {
mManager = new MultiSelectManager(mAdapter, mEventHelper, MultiSelectManager.MODE_SINGLE);
mManager.addCallback(mCallback);
- tap(13);
+ longPress(13);
shiftTap(20);
assertSelection(20);
}
@@ -236,20 +235,24 @@
assertSelection(2, 3, 4);
}
+ private void longPress(int position) {
+ mManager.onLongPress(TestInputEvent.tap(position));
+ }
+
private void tap(int position) {
- mManager.onSingleTapUp(position, 0, MotionEvent.TOOL_TYPE_MOUSE);
+ mManager.onSingleTapUp(TestInputEvent.tap(position));
}
private void shiftTap(int position) {
- mManager.onSingleTapUp(position, KeyEvent.META_SHIFT_ON, MotionEvent.TOOL_TYPE_FINGER);
+ mManager.onSingleTapUp(TestInputEvent.shiftTap(position));
}
private void click(int position) {
- mManager.onSingleTapUp(position, 0, MotionEvent.TOOL_TYPE_MOUSE);
+ mManager.onSingleTapUp(TestInputEvent.click(position));
}
private void shiftClick(int position) {
- mManager.onSingleTapUp(position, KeyEvent.META_SHIFT_ON, MotionEvent.TOOL_TYPE_MOUSE);
+ mManager.onSingleTapUp(TestInputEvent.shiftClick(position));
}
private void assertSelected(int... expected) {
@@ -282,10 +285,10 @@
assertEquals(selection.toString(), expected, selection.size());
}
- private static final class EventHelper implements MultiSelectManager.MultiSelectHelper {
+ private static final class EventHelper implements MultiSelectManager.ItemFinder {
@Override
- public int findEventPosition(MotionEvent e) {
+ public int findItemPosition(MotionEvent e) {
throw new UnsupportedOperationException();
}
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java
similarity index 80%
rename from packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectModelTest.java
rename to packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java
index 20c4548..87d7e15 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java
@@ -16,25 +16,26 @@
package com.android.documentsui;
-import static org.junit.Assert.*;
-
-import com.android.documentsui.MultiSelectManager.BandSelectModel;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.graphics.Point;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.util.SparseBooleanArray;
+import com.android.documentsui.MultiSelectManager.GridModel;
+
import org.junit.After;
import org.junit.Test;
-public class BandSelectModelTest {
+public class MultiSelectManager_GridModelTest {
private static final int VIEW_PADDING_PX = 5;
private static final int CHILD_VIEW_EDGE_PX = 100;
private static final int VIEWPORT_HEIGHT = 500;
- private static BandSelectModel model;
+ private static GridModel model;
private static TestHelper helper;
private static SparseBooleanArray lastSelection;
private static int viewWidth;
@@ -42,14 +43,14 @@
private static void setUp(int numChildren, int numColumns) {
helper = new TestHelper(numChildren, numColumns);
viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX);
- model = new BandSelectModel(helper);
- model.addOnSelectionChangedListener(new BandSelectModel.OnSelectionChangedListener() {
-
- @Override
- public void onSelectionChanged(SparseBooleanArray updatedSelection) {
- lastSelection = updatedSelection;
- }
- });
+ model = new GridModel(helper);
+ model.addOnSelectionChangedListener(
+ new GridModel.OnSelectionChangedListener() {
+ @Override
+ public void onSelectionChanged(SparseBooleanArray updatedSelection) {
+ lastSelection = updatedSelection;
+ }
+ });
}
@After
@@ -65,7 +66,7 @@
model.startSelection(new Point(0, 10));
model.resizeSelection(new Point(1, 11));
assertSelected(new int[0]);
- assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
@@ -74,7 +75,7 @@
model.startSelection(new Point(viewWidth - 1, 10));
model.resizeSelection(new Point(viewWidth - 2, 11));
assertSelected(new int[0]);
- assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
@@ -83,7 +84,7 @@
model.startSelection(new Point(10, 0));
model.resizeSelection(new Point(11, 1));
assertSelected(new int[0]);
- assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
@@ -92,7 +93,7 @@
model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
model.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
assertSelected(new int[0]);
- assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
@@ -101,7 +102,7 @@
model.startSelection(new Point(106, 0));
model.resizeSelection(new Point(107, 200));
assertSelected(new int[0]);
- assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
@@ -110,7 +111,7 @@
model.startSelection(new Point(0, 105));
model.resizeSelection(new Point(200, 106));
assertSelected(new int[0]);
- assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
@@ -141,7 +142,7 @@
assertSelected(new int[] {0});
model.resizeSelection(new Point(0, 0));
assertSelected(new int[0]);
- assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
+ assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
@@ -191,7 +192,7 @@
model.onScrolled(null, 0, dy);
}
- private static final class TestHelper implements MultiSelectManager.BandModelHelper {
+ private static final class TestHelper implements MultiSelectManager.BandEnvironment {
public int horizontalOffset = 0;
public int verticalOffset = 0;
@@ -269,18 +270,53 @@
}
@Override
- public int getTotalChildCount() {
+ public int getChildCount() {
return mNumChildren;
}
@Override
- public int getNumColumns() {
+ public int getColumnCount() {
return mNumColumns;
}
@Override
- public int getNumRows() {
+ public int getRowCount() {
return mNumRows;
}
+
+ @Override
+ public void showBand(Rect rect) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void hideBand() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void scrollBy(int dy) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getHeight() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void invalidateView() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void runAtNextFrame(Runnable r) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeCallback(Runnable r) {
+ throw new UnsupportedOperationException();
+ }
}
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java b/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java
new file mode 100644
index 0000000..e83f9e0
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java
@@ -0,0 +1,90 @@
+package com.android.documentsui;
+
+import android.graphics.Point;
+import android.support.v7.widget.RecyclerView;
+
+class TestInputEvent implements Events.InputEvent {
+
+ public boolean mouseEvent;
+ public boolean primaryButtonPressed;
+ public boolean secondaryButtonPressed;
+ public boolean shiftKeyDow;
+ public boolean actionDown;
+ public boolean actionUp;
+ public Point location;
+ public int position = Integer.MIN_VALUE;
+
+ public TestInputEvent() {}
+
+ public TestInputEvent(int position) {
+ this.position = position;
+ }
+
+ @Override
+ public boolean isMouseEvent() {
+ return mouseEvent;
+ }
+
+ @Override
+ public boolean isPrimaryButtonPressed() {
+ return primaryButtonPressed;
+ }
+
+ @Override
+ public boolean isSecondaryButtonPressed() {
+ return secondaryButtonPressed;
+ }
+
+ @Override
+ public boolean isShiftKeyDown() {
+ return shiftKeyDow;
+ }
+
+ @Override
+ public boolean isActionDown() {
+ return actionDown;
+ }
+
+ @Override
+ public boolean isActionUp() {
+ return actionUp;
+ }
+
+ @Override
+ public Point getOrigin() {
+ return location;
+ }
+
+ @Override
+ public boolean isOverItem() {
+ return position != Integer.MIN_VALUE && position != RecyclerView.NO_POSITION;
+ }
+
+ @Override
+ public int getItemPosition() {
+ return position;
+ }
+
+ public static TestInputEvent tap(int position) {
+ return new TestInputEvent(position);
+ }
+
+ public static TestInputEvent shiftTap(int position) {
+ TestInputEvent e = new TestInputEvent(position);
+ e.shiftKeyDow = true;
+ return e;
+ }
+
+ public static TestInputEvent click(int position) {
+ TestInputEvent e = new TestInputEvent(position);
+ e.mouseEvent = true;
+ return e;
+ }
+
+ public static TestInputEvent shiftClick(int position) {
+ TestInputEvent e = new TestInputEvent(position);
+ e.mouseEvent = true;
+ e.shiftKeyDow = true;
+ return e;
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java
index d90130f..be3f251 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java
@@ -22,7 +22,7 @@
@RunWith(Suite.class)
@SuiteClasses({
- BandSelectModelTest.class,
+ MultiSelectManager_GridModelTest.class,
MultiSelectManager_SelectionTest.class,
MultiSelectManagerTest.class
})
diff --git a/packages/Keyguard/res/values-ja/strings.xml b/packages/Keyguard/res/values-ja/strings.xml
index 502a2ed..cea4319 100644
--- a/packages/Keyguard/res/values-ja/strings.xml
+++ b/packages/Keyguard/res/values-ja/strings.xml
@@ -112,7 +112,7 @@
<string name="airplane_mode" msgid="3122107900897202805">"機内モード"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="489430505491862444">"端末を再起動するにはパターンが必要です。"</string>
<string name="kg_prompt_reason_restart_pin" msgid="994878216570694974">"端末を再起動するにはPINが必要です。"</string>
- <string name="kg_prompt_reason_restart_password" msgid="2375742919528461664">"端末を再起動するにはパスワードが必要です。"</string>
+ <string name="kg_prompt_reason_restart_password" msgid="2375742919528461664">"端末を再起動した時にはパスワードが必要です。"</string>
<string name="kg_prompt_reason_timeout_pattern" msgid="8930047492617900785">"セキュリティを強化するため、パターンが必要です。"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="7470468607947726377">"セキュリティを強化するため、PINが必要です。"</string>
<string name="kg_prompt_reason_timeout_password" msgid="1177412542773936957">"セキュリティを強化するため、パスワードが必要です。"</string>
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/KeyguardPinBasedInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
index ce2d11a..f51e10f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -64,7 +64,7 @@
@Override
protected void resetState() {
- mPasswordEntry.setEnabled(true);
+ setPasswordEntryEnabled(true);
}
@Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index fc6117f..47d8e28 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -29,6 +29,7 @@
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.graphics.Bitmap;
+import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -472,6 +473,10 @@
}
}
+ private void handleFingerprintLockoutReset() {
+ updateFingerprintListeningState();
+ }
+
private void setFingerprintRunningState(int fingerprintRunningState) {
boolean wasRunning = mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
boolean isRunning = fingerprintRunningState == FINGERPRINT_STATE_RUNNING;
@@ -681,6 +686,14 @@
}
};
+ private final FingerprintManager.LockoutResetCallback mLockoutResetCallback
+ = new FingerprintManager.LockoutResetCallback() {
+ @Override
+ public void onLockoutReset() {
+ handleFingerprintLockoutReset();
+ }
+ };
+
private FingerprintManager.AuthenticationCallback mAuthenticationCallback
= new AuthenticationCallback() {
@@ -1003,6 +1016,9 @@
mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
updateFingerprintListeningState();
+ if (mFpm != null) {
+ mFpm.addLockoutResetCallback(mLockoutResetCallback);
+ }
}
private void updateFingerprintListeningState() {
@@ -1016,7 +1032,7 @@
}
private boolean shouldListenForFingerprint() {
- return (mKeyguardIsVisible || !mDeviceInteractive) && !mSwitchingUser
+ return (mKeyguardIsVisible || !mDeviceInteractive || mBouncer) && !mSwitchingUser
&& !mFingerprintAlreadyAuthenticated && !isFingerprintDisabled(getCurrentUser());
}
@@ -1332,9 +1348,7 @@
*/
private void handleKeyguardReset() {
if (DEBUG) Log.d(TAG, "handleKeyguardReset");
- if (!isUnlockingWithFingerprintAllowed()) {
- updateFingerprintListeningState();
- }
+ updateFingerprintListeningState();
}
/**
@@ -1351,6 +1365,7 @@
cb.onKeyguardBouncerChanged(isBouncer);
}
}
+ updateFingerprintListeningState();
}
/**
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
new file mode 100644
index 0000000..b5694b7
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
@@ -0,0 +1,100 @@
+/*
+ * 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.mtp;
+
+import android.database.MatrixCursor;
+import android.mtp.MtpConstants;
+import android.mtp.MtpObjectInfo;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+
+import java.util.Date;
+
+final class CursorHelper {
+ static final int DUMMY_HANDLE_FOR_ROOT = 0;
+
+ private CursorHelper() {
+ }
+
+ static void addToCursor(MtpRoot root, MatrixCursor.RowBuilder builder) {
+ final Identifier identifier = new Identifier(
+ root.mDeviceId, root.mStorageId, DUMMY_HANDLE_FOR_ROOT);
+ builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId());
+ builder.add(Document.COLUMN_DISPLAY_NAME, root.mDescription);
+ builder.add(Document.COLUMN_MIME_TYPE, DocumentsContract.Document.MIME_TYPE_DIR);
+ builder.add(Document.COLUMN_LAST_MODIFIED, null);
+ builder.add(Document.COLUMN_FLAGS, 0);
+ builder.add(Document.COLUMN_SIZE,
+ (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE));
+ }
+
+ static void addToCursor(MtpObjectInfo objectInfo, Identifier rootIdentifier,
+ MatrixCursor.RowBuilder builder) {
+ final Identifier identifier = new Identifier(
+ rootIdentifier.mDeviceId, rootIdentifier.mStorageId, objectInfo.getObjectHandle());
+ final String mimeType = formatTypeToMimeType(objectInfo.getFormat());
+
+ int flag = 0;
+ if (objectInfo.getProtectionStatus() == 0) {
+ flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+ DocumentsContract.Document.FLAG_SUPPORTS_WRITE;
+ if (mimeType == DocumentsContract.Document.MIME_TYPE_DIR) {
+ flag |= DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
+ }
+ }
+ if (objectInfo.getThumbCompressedSize() > 0) {
+ flag |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL;
+ }
+
+ builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId());
+ builder.add(Document.COLUMN_DISPLAY_NAME, objectInfo.getName());
+ builder.add(Document.COLUMN_MIME_TYPE, mimeType);
+ builder.add(
+ Document.COLUMN_LAST_MODIFIED,
+ objectInfo.getDateModified() != 0 ? objectInfo.getDateModified() : null);
+ builder.add(Document.COLUMN_FLAGS, flag);
+ builder.add(Document.COLUMN_SIZE, objectInfo.getCompressedSize());
+ }
+
+ static String formatTypeToMimeType(int format) {
+ // TODO: Add complete list of mime types.
+ switch (format) {
+ case MtpConstants.FORMAT_ASSOCIATION:
+ return DocumentsContract.Document.MIME_TYPE_DIR;
+ case MtpConstants.FORMAT_MP3:
+ return "audio/mp3";
+ case MtpConstants.FORMAT_EXIF_JPEG:
+ return "image/jpeg";
+ default:
+ return "application/octet-stream";
+ }
+ }
+
+ static int mimeTypeToFormatType(String mimeType) {
+ // TODO: Add complete list of mime types.
+ switch (mimeType.toLowerCase()) {
+ case Document.MIME_TYPE_DIR:
+ return MtpConstants.FORMAT_ASSOCIATION;
+ case "audio/mp3":
+ return MtpConstants.FORMAT_MP3;
+ case "image/jpeg":
+ return MtpConstants.FORMAT_EXIF_JPEG;
+ default:
+ return MtpConstants.FORMAT_UNDEFINED;
+ }
+ }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index c430def..0d4265a 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -19,6 +19,7 @@
import android.content.ContentResolver;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
@@ -34,6 +35,7 @@
* Loader for MTP document.
* At the first request, the loader returns only first NUM_INITIAL_ENTRIES. Then it launches
* background thread to load the rest documents and caches its result for next requests.
+ * TODO: Rename this class to ObjectInfoLoader
*/
class DocumentLoader {
static final int NUM_INITIAL_ENTRIES = 10;
@@ -50,13 +52,13 @@
mResolver = resolver;
}
- private static MtpDocument[] loadDocuments(MtpManager manager, int deviceId, int[] handles)
+ private static MtpObjectInfo[] loadDocuments(MtpManager manager, int deviceId, int[] handles)
throws IOException {
- final MtpDocument[] documents = new MtpDocument[handles.length];
+ final MtpObjectInfo[] objectInfos = new MtpObjectInfo[handles.length];
for (int i = 0; i < handles.length; i++) {
- documents[i] = manager.getDocument(deviceId, handles[i]);
+ objectInfos[i] = manager.getObjectInfo(deviceId, handles[i]);
}
- return documents;
+ return objectInfos;
}
synchronized Cursor queryChildDocuments(String[] columnNames, Identifier parent)
@@ -66,7 +68,7 @@
int parentHandle = parent.mObjectHandle;
// Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to
// getObjectHandles if we would like to obtain children under the root.
- if (parentHandle == MtpDocument.DUMMY_HANDLE_FOR_ROOT) {
+ if (parentHandle == CursorHelper.DUMMY_HANDLE_FOR_ROOT) {
parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
}
task = new LoaderTask(parent, mMtpManager.getObjectHandles(
@@ -89,12 +91,16 @@
return task.createCursor(mResolver, columnNames);
}
- synchronized void clearCache(int deviceId) {
- mTaskList.clearTaskForDevice(deviceId);
+ synchronized void clearTasks() {
+ mTaskList.clear();
}
- synchronized void clearCache() {
- mTaskList.clearCompletedTask();
+ synchronized void clearCompletedTasks() {
+ mTaskList.clearCompletedTasks();
+ }
+
+ synchronized void clearTask(Identifier parentIdentifier) {
+ mTaskList.clearTask(parentIdentifier);
}
private class BackgroundLoaderThread extends Thread {
@@ -114,16 +120,16 @@
deviceId = task.mIdentifier.mDeviceId;
handles = task.getUnloadedObjectHandles(NUM_LOADING_ENTRIES);
}
- MtpDocument[] documents;
+ MtpObjectInfo[] objectInfos;
try {
- documents = loadDocuments(mMtpManager, deviceId, handles);
+ objectInfos = loadDocuments(mMtpManager, deviceId, handles);
} catch (IOException exception) {
- documents = null;
+ objectInfos = null;
Log.d(MtpDocumentsProvider.TAG, exception.getMessage());
}
synchronized (DocumentLoader.this) {
- if (documents != null) {
- task.fillDocuments(documents);
+ if (objectInfos != null) {
+ task.fillDocuments(objectInfos);
final boolean shouldNotify =
task.mLastNotified.getTime() <
new Date().getTime() - NOTIFY_PERIOD_MS ||
@@ -156,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()) {
@@ -177,19 +172,30 @@
}
}
}
+
+ void clearTask(Identifier parentIdentifier) {
+ for (int i = 0; i < size(); i++) {
+ final LoaderTask task = get(i);
+ if (task.mIdentifier.mDeviceId == parentIdentifier.mDeviceId &&
+ task.mIdentifier.mObjectHandle == parentIdentifier.mObjectHandle) {
+ remove(i);
+ return;
+ }
+ }
+ }
}
private static class LoaderTask {
final Identifier mIdentifier;
final int[] mObjectHandles;
- final MtpDocument[] mDocuments;
+ final MtpObjectInfo[] mObjectInfos;
Date mLastNotified;
int mNumLoaded;
LoaderTask(Identifier identifier, int[] objectHandles) {
mIdentifier = identifier;
mObjectHandles = objectHandles;
- mDocuments = new MtpDocument[mObjectHandles.length];
+ mObjectInfos = new MtpObjectInfo[mObjectHandles.length];
mNumLoaded = 0;
mLastNotified = new Date();
}
@@ -199,7 +205,7 @@
final Identifier rootIdentifier = new Identifier(
mIdentifier.mDeviceId, mIdentifier.mStorageId);
for (int i = 0; i < mNumLoaded; i++) {
- mDocuments[i].addToCursor(rootIdentifier, cursor.newRow());
+ CursorHelper.addToCursor(mObjectInfos[i], rootIdentifier, cursor.newRow());
}
final Bundle extras = new Bundle();
extras.putBoolean(DocumentsContract.EXTRA_LOADING, !completed());
@@ -209,7 +215,7 @@
}
boolean completed() {
- return mNumLoaded == mDocuments.length;
+ return mNumLoaded == mObjectInfos.length;
}
int[] getUnloadedObjectHandles(int count) {
@@ -224,9 +230,9 @@
mLastNotified = new Date();
}
- void fillDocuments(MtpDocument[] documents) {
- for (int i = 0; i < documents.length; i++) {
- mDocuments[mNumLoaded++] = documents[i];
+ void fillDocuments(MtpObjectInfo[] objectInfos) {
+ for (int i = 0; i < objectInfos.length; i++) {
+ mObjectInfos[mNumLoaded++] = objectInfos[i];
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java
index bd0c837..ae29f52 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java
@@ -41,7 +41,7 @@
Identifier(int deviceId, int storageId) {
- this(deviceId, storageId, MtpDocument.DUMMY_HANDLE_FOR_ROOT);
+ this(deviceId, storageId, CursorHelper.DUMMY_HANDLE_FOR_ROOT);
}
Identifier(int deviceId, int storageId, int objectHandle) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java
deleted file mode 100644
index c1d9609..0000000
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java
+++ /dev/null
@@ -1,132 +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.
- */
-
-package com.android.mtp;
-
-import android.database.MatrixCursor;
-import android.mtp.MtpObjectInfo;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
-
-import java.util.Date;
-
-class MtpDocument {
- static final int DUMMY_HANDLE_FOR_ROOT = 0;
-
- private final int mObjectHandle;
- private final int mFormat;
- private final String mName;
- private final Date mDateModified;
- private final int mSize;
- private final int mThumbSize;
- private final boolean mReadOnly;
-
- /**
- * Constructor for root document.
- */
- MtpDocument(MtpRoot root) {
- this(DUMMY_HANDLE_FOR_ROOT,
- 0x3001, // Directory.
- root.mDescription,
- null, // Unknown name.
- (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE),
- 0, // Total size.
- true); // Writable.
- }
-
- MtpDocument(MtpObjectInfo objectInfo) {
- this(objectInfo.getObjectHandle(),
- objectInfo.getFormat(),
- objectInfo.getName(),
- objectInfo.getDateModified() != 0 ? new Date(objectInfo.getDateModified()) : null,
- objectInfo.getCompressedSize(),
- objectInfo.getThumbCompressedSize(),
- objectInfo.getProtectionStatus() != 0);
- }
-
- MtpDocument(int objectHandle,
- int format,
- String name,
- Date dateModified,
- int size,
- int thumbSize,
- boolean readOnly) {
- this.mObjectHandle = objectHandle;
- this.mFormat = format;
- this.mName = name;
- this.mDateModified = dateModified;
- this.mSize = size;
- this.mThumbSize = thumbSize;
- this.mReadOnly = readOnly;
- }
-
- void addToCursor(Identifier rootIdentifier, MatrixCursor.RowBuilder builder) {
- final Identifier identifier = new Identifier(
- rootIdentifier.mDeviceId, rootIdentifier.mStorageId, mObjectHandle);
- final String mimeType = formatTypeToMimeType(mFormat);
-
- int flag = 0;
- if (mObjectHandle != DUMMY_HANDLE_FOR_ROOT) {
- if (mThumbSize > 0) {
- flag |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL;
- }
- if (!mReadOnly) {
- flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
- DocumentsContract.Document.FLAG_SUPPORTS_WRITE;
- }
- }
- if (mimeType == DocumentsContract.Document.MIME_TYPE_DIR && !mReadOnly) {
- flag |= DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
- }
-
- builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId());
- builder.add(Document.COLUMN_DISPLAY_NAME, mName);
- builder.add(Document.COLUMN_MIME_TYPE, mimeType);
- builder.add(
- Document.COLUMN_LAST_MODIFIED,
- mDateModified != null ? mDateModified.getTime() : null);
- builder.add(Document.COLUMN_FLAGS, flag);
- builder.add(Document.COLUMN_SIZE, mSize);
- }
-
- static String formatTypeToMimeType(int format) {
- // TODO: Add complete list of mime types.
- switch (format) {
- case 0x3001:
- return DocumentsContract.Document.MIME_TYPE_DIR;
- case 0x3009:
- return "audio/mp3";
- case 0x3801:
- return "image/jpeg";
- default:
- return "application/octet-stream";
- }
- }
-
- static int mimeTypeToFormatType(String mimeType) {
- // TODO: Add complete list of mime types.
- switch (mimeType.toLowerCase()) {
- case Document.MIME_TYPE_DIR:
- return 0x3001;
- case "audio/mp3":
- return 0x3009;
- case "image/jpeg":
- return 0x3801;
- default:
- return 0x3000; // Undefined object.
- }
- }
-}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index a3cf3e2..a1a43c1 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -21,11 +21,12 @@
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.Point;
+import android.mtp.MtpObjectInfo;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import android.util.Log;
@@ -33,6 +34,8 @@
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
/**
* DocumentsProvider for MTP devices.
@@ -55,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;
/**
@@ -71,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;
}
@@ -81,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);
}
@@ -116,37 +118,37 @@
}
final Identifier identifier = Identifier.createFromDocumentId(documentId);
- MtpDocument document = null;
- if (identifier.mObjectHandle != MtpDocument.DUMMY_HANDLE_FOR_ROOT) {
+ if (identifier.mObjectHandle != CursorHelper.DUMMY_HANDLE_FOR_ROOT) {
+ MtpObjectInfo objectInfo;
try {
- document = mMtpManager.getDocument(identifier.mDeviceId, identifier.mObjectHandle);
+ objectInfo = mMtpManager.getObjectInfo(
+ identifier.mDeviceId, identifier.mObjectHandle);
} catch (IOException e) {
throw new FileNotFoundException(e.getMessage());
}
+ final MatrixCursor cursor = new MatrixCursor(projection);
+ CursorHelper.addToCursor(
+ objectInfo,
+ new Identifier(identifier.mDeviceId, identifier.mStorageId),
+ cursor.newRow());
+ return cursor;
} else {
MtpRoot[] roots;
try {
roots = mMtpManager.getRoots(identifier.mDeviceId);
- if (roots != null) {
- for (final MtpRoot root : roots) {
- if (identifier.mStorageId == root.mStorageId) {
- document = new MtpDocument(root);
- break;
- }
- }
- }
- if (document == null) {
- throw new FileNotFoundException();
- }
} catch (IOException e) {
throw new FileNotFoundException(e.getMessage());
}
+ for (final MtpRoot root : roots) {
+ if (identifier.mStorageId != root.mStorageId)
+ continue;
+ final MatrixCursor cursor = new MatrixCursor(projection);
+ CursorHelper.addToCursor(root, cursor.newRow());
+ return cursor;
+ }
}
- final MatrixCursor cursor = new MatrixCursor(projection);
- document.addToCursor(
- new Identifier(identifier.mDeviceId, identifier.mStorageId), cursor.newRow());
- return cursor;
+ throw new FileNotFoundException();
}
@Override
@@ -157,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());
}
@@ -171,9 +174,12 @@
try {
switch (mode) {
case "r":
- return mPipeManager.readDocument(mMtpManager, identifier);
+ return getPipeManager(identifier).readDocument(mMtpManager, identifier);
case "w":
- return mPipeManager.writeDocument(getContext(), mMtpManager, identifier);
+ // TODO: Clear the parent document loader task (if exists) and call notify
+ // when writing is completed.
+ return getPipeManager(identifier).writeDocument(
+ getContext(), mMtpManager, identifier);
default:
// TODO: Add support for seekable files.
throw new UnsupportedOperationException(
@@ -192,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) {
@@ -207,8 +213,10 @@
final int parentHandle =
mMtpManager.getParent(identifier.mDeviceId, identifier.mObjectHandle);
mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
- notifyChildDocumentsChange(new Identifier(
- identifier.mDeviceId, identifier.mStorageId, parentHandle).toDocumentId());
+ final Identifier parentIdentifier = new Identifier(
+ identifier.mDeviceId, identifier.mStorageId, parentHandle);
+ getDocumentLoader(parentIdentifier).clearTask(parentIdentifier);
+ notifyChildDocumentsChange(parentIdentifier.toDocumentId());
} catch (IOException error) {
throw new FileNotFoundException(error.getMessage());
}
@@ -216,7 +224,9 @@
@Override
public void onTrimMemory(int level) {
- mDocumentLoader.clearCache();
+ for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
+ toolkit.mDocumentLoader.clearCompletedTasks();
+ }
}
@Override
@@ -224,11 +234,19 @@
throws FileNotFoundException {
try {
final Identifier parentId = Identifier.createFromDocumentId(parentDocumentId);
+ final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe();
+ pipe[0].close(); // 0 bytes for a new document.
final int objectHandle = mMtpManager.createDocument(
- parentId.mDeviceId, parentId.mStorageId, parentId.mObjectHandle, mimeType,
- displayName);
- final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId,
+ parentId.mDeviceId,
+ new MtpObjectInfo.Builder()
+ .setStorageId(parentId.mStorageId)
+ .setParent(parentId.mObjectHandle)
+ .setFormat(CursorHelper.mimeTypeToFormatType(mimeType))
+ .setName(displayName)
+ .build(), pipe[1]);
+ final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId,
objectHandle).toDocumentId();
+ getDocumentLoader(parentId).clearTask(parentId);
notifyChildDocumentsChange(parentDocumentId);
return documentId;
} catch (IOException error) {
@@ -239,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.clearCache(deviceId);
mRootScanner.scanNow();
}
@@ -253,7 +274,7 @@
for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
try {
mMtpManager.closeDevice(deviceId);
- mDocumentLoader.clearCache(deviceId);
+ getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
closed = true;
} catch (IOException d) {
Log.d(TAG, "Failed to close the MTP device: " + deviceId);
@@ -274,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 6647009..7cc7413 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -20,6 +20,7 @@
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
+import android.mtp.MtpConstants;
import android.mtp.MtpDevice;
import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
@@ -41,7 +42,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 {
@@ -95,94 +96,96 @@
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) throws IOException {
- final MtpDevice device = getDevice(deviceId);
- return device.getObjectInfo(objectHandle);
- }
-
- synchronized MtpDocument getDocument(int deviceId, int objectHandle) throws IOException {
- final MtpDevice device = getDevice(deviceId);
- return new MtpDocument(device.getObjectInfo(objectHandle));
- }
-
- synchronized int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
+ MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
throws IOException {
final MtpDevice device = getDevice(deviceId);
- return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
+ synchronized (device) {
+ return device.getObjectInfo(objectHandle);
+ }
}
- synchronized byte[] getObject(int deviceId, int objectHandle, int expectedSize)
+ int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
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.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
}
}
- // TODO: Remove this method.
- synchronized int createDocument(int deviceId, int storageId, int parentObjectHandle,
- String mimeType, String name) throws IOException {
- final MtpObjectInfo objectInfo = new MtpObjectInfo.Builder()
- .setName(name)
- .setStorageId(storageId)
- .setParent(parentObjectHandle)
- .setFormat(MtpDocument.mimeTypeToFormatType(mimeType))
- .build();
- return createDocument(deviceId, objectInfo);
- }
-
- synchronized int createDocument(int deviceId, MtpObjectInfo objectInfo) throws IOException {
- final MtpDevice device = getDevice(deviceId);
- final MtpObjectInfo result = device.sendObjectInfo(objectInfo);
- if (result == null) {
- throw new IOException("Failed to create a document");
- }
- return result.getObjectHandle();
- }
-
- synchronized int getParent(int deviceId, int objectHandle) throws IOException {
- final MtpDevice device = getDevice(deviceId);
- final int result = (int) device.getParent(objectHandle);
- if (result < 0) {
- throw new FileNotFoundException("Not found parent object");
- }
- return result;
- }
-
- synchronized void importFile(int deviceId, int objectHandle, ParcelFileDescriptor target)
+ byte[] getObject(int deviceId, int objectHandle, int expectedSize)
throws IOException {
final MtpDevice device = getDevice(deviceId);
- device.importFile(objectHandle, target);
+ synchronized (device) {
+ return device.getObject(objectHandle, expectedSize);
+ }
}
- synchronized void sendObject(int deviceId, int objectHandle, int size,
+ 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");
+ }
+ }
+ }
+
+ int createDocument(int deviceId, MtpObjectInfo objectInfo,
ParcelFileDescriptor source) throws IOException {
final MtpDevice device = getDevice(deviceId);
- if (!device.sendObject(objectHandle, size, source))
- throw new IOException("Failed to send a document");
+ 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();
+ }
+ }
+
+ 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);
+ synchronized (device) {
+ device.importFile(objectHandle, target);
+ }
}
private MtpDevice getDevice(int deviceId) throws IOException {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
index 53f1b65..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) {
@@ -139,20 +139,15 @@
// Delete the target object info if it already exists (as a placeholder).
mManager.deleteDocument(mIdentifier.mDeviceId, mIdentifier.mObjectHandle);
- // Create the target object info with a correct file size.
- final int targetObjectHandle =
- mManager.createDocument(
- mIdentifier.mDeviceId,
- new MtpObjectInfo.Builder(placeholderObjectInfo)
- .setCompressedSize((int) tempFile.length())
- .build());
-
- // Upload the object.
+ // Create the target object info with a correct file size and upload the file.
+ final MtpObjectInfo targetObjectInfo =
+ new MtpObjectInfo.Builder(placeholderObjectInfo)
+ .setCompressedSize((int) tempFile.length())
+ .build();
final ParcelFileDescriptor tempInputDescriptor = ParcelFileDescriptor.open(
tempFile, ParcelFileDescriptor.MODE_READ_ONLY);
- mManager.sendObject(mIdentifier.mDeviceId,
- targetObjectHandle, (int) tempFile.length(), tempInputDescriptor);
-
+ mManager.createDocument(mIdentifier.mDeviceId,
+ targetObjectInfo, tempInputDescriptor);
} catch (IOException error) {
Log.w(MtpDocumentsProvider.TAG,
"Failed to send a file because of: " + error.getMessage());
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index 1e015bd..a012d7f 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.database.Cursor;
+import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.test.AndroidTestCase;
@@ -31,14 +32,14 @@
@SmallTest
public class DocumentLoaderTest extends AndroidTestCase {
- private BlockableTestMtpMaanger mManager;
+ private BlockableTestMtpManager mManager;
private TestContentResolver mResolver;
private DocumentLoader mLoader;
final private Identifier mParentIdentifier = new Identifier(0, 0, 0);
@Override
public void setUp() {
- mManager = new BlockableTestMtpMaanger(getContext());
+ mManager = new BlockableTestMtpManager(getContext());
mResolver = new TestContentResolver();
mLoader = new DocumentLoader(mManager, mResolver);
}
@@ -85,22 +86,17 @@
for (int i = 0; i < childDocuments.length; i++) {
final int objectHandle = i + 1;
childDocuments[i] = objectHandle;
- manager.setDocument(0, objectHandle, new MtpDocument(
- objectHandle,
- 0 /* format */,
- "file" + objectHandle,
- new Date(),
- 1024,
- 0 /* thumbnail size */,
- false /* not read only */));
+ manager.setObjectInfo(0, new MtpObjectInfo.Builder()
+ .setObjectHandle(objectHandle)
+ .build());
}
manager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, childDocuments);
}
- private static class BlockableTestMtpMaanger extends TestMtpManager {
+ private static class BlockableTestMtpManager extends TestMtpManager {
final private Map<String, CountDownLatch> blockedDocuments = new HashMap<>();
- BlockableTestMtpMaanger(Context context) {
+ BlockableTestMtpManager(Context context) {
super(context);
}
@@ -113,7 +109,7 @@
}
@Override
- MtpDocument getDocument(int deviceId, int objectHandle) throws IOException {
+ MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
final CountDownLatch latch = blockedDocuments.get(pack(deviceId, objectHandle));
if (latch != null) {
try {
@@ -122,7 +118,7 @@
fail();
}
}
- return super.getDocument(deviceId, objectHandle);
+ return super.getObjectInfo(deviceId, objectHandle);
}
}
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 1826bd0..4b3a5cd 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -17,9 +17,12 @@
package com.android.mtp;
import android.database.Cursor;
+import android.mtp.MtpConstants;
+import android.mtp.MtpObjectInfo.Builder;
+import android.mtp.MtpObjectInfo;
import android.net.Uri;
-import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsContract;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
@@ -36,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();
@@ -204,14 +207,16 @@
}
public void testQueryDocument() throws IOException {
- mMtpManager.setDocument(0, 2, new MtpDocument(
- 2 /* object handle */,
- 0x3801 /* JPEG */,
- "image.jpg" /* display name */,
- new Date(1422716400000L) /* modified date */,
- 1024 * 1024 * 5 /* file size */,
- 1024 * 50 /* thumbnail size */,
- false /* read only */));
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
+ mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
+ .setObjectHandle(2)
+ .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
+ .setName("image.jpg")
+ .setDateModified(1422716400000L)
+ .setCompressedSize(1024 * 1024 * 5)
+ .setThumbCompressedSize(1024 * 50)
+ .build());
final Cursor cursor = mProvider.queryDocument("0_1_2", null);
assertEquals(1, cursor.getCount());
@@ -229,14 +234,14 @@
}
public void testQueryDocument_directory() throws IOException {
- mMtpManager.setDocument(0, 2, new MtpDocument(
- 2 /* object handle */,
- 0x3001 /* directory */,
- "directory" /* display name */,
- new Date(1422716400000L) /* modified date */,
- 0 /* file size */,
- 0 /* thumbnail size */,
- false /* read only */));
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
+ mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
+ .setObjectHandle(2)
+ .setFormat(MtpConstants.FORMAT_ASSOCIATION)
+ .setName("directory")
+ .setDateModified(1422716400000L)
+ .build());
final Cursor cursor = mProvider.queryDocument("0_1_2", null);
assertEquals(1, cursor.getCount());
@@ -254,6 +259,8 @@
}
public void testQueryDocument_forRoot() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setRoots(0, new MtpRoot[] {
new MtpRoot(
0 /* deviceId */,
@@ -276,16 +283,18 @@
}
public void testQueryChildDocuments() throws Exception {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
- mMtpManager.setDocument(0, 1, new MtpDocument(
- 1 /* object handle */,
- 0x3801 /* JPEG */,
- "image.jpg" /* display name */,
- new Date(0) /* modified date */,
- 1024 * 1024 * 5 /* file size */,
- 1024 * 50 /* thumbnail size */,
- true /* read only */));
+ mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
+ .setObjectHandle(1)
+ .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
+ .setName("image.jpg")
+ .setCompressedSize(1024 * 1024 * 5)
+ .setThumbCompressedSize(5 * 1024)
+ .setProtectionStatus(MtpConstants.PROTECTION_STATUS_READ_ONLY)
+ .build());
final Cursor cursor = mProvider.queryChildDocuments("0_0_0", null, null);
assertEquals(1, cursor.getCount());
@@ -302,6 +311,8 @@
}
public void testQueryChildDocuments_cursorError() throws Exception {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
try {
mProvider.queryChildDocuments("0_0_0", null, null);
fail();
@@ -311,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);
@@ -320,24 +333,25 @@
}
}
- public void testDeleteDocument() throws FileNotFoundException {
- mMtpManager.setDocument(0, 1, new MtpDocument(
- 1 /* object handle */,
- 0x3801 /* JPEG */,
- "image.jpg" /* display name */,
- new Date(1422716400000L) /* modified date */,
- 1024 * 1024 * 5 /* file size */,
- 1024 * 50 /* thumbnail size */,
- false /* not read only */));
- mMtpManager.setParent(0, 1, 2);
+ public void testDeleteDocument() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
+ mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
+ .setObjectHandle(1)
+ .setParent(2)
+ .build());
mProvider.deleteDocument("0_0_1");
assertEquals(1, mResolver.getChangeCount(
DocumentsContract.buildChildDocumentsUri(
MtpDocumentsProvider.AUTHORITY, "0_0_2")));
}
- public void testDeleteDocument_error() {
- mMtpManager.setParent(0, 1, 2);
+ public void testDeleteDocument_error() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
+ mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
+ .setObjectHandle(2)
+ .build());
try {
mProvider.deleteDocument("0_0_1");
fail();
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
index cfb8b04..53018cc 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
@@ -16,6 +16,7 @@
package com.android.mtp;
+import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
@@ -56,7 +57,9 @@
public void testWriteDocument_basic() throws Exception {
// Create a placeholder file which should be replaced by a real file later.
- mtpManager.setDocument(0, 1, new MtpDocument(1, 0, "", new Date(), 0, 0, false));
+ mtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
+ .setObjectHandle(1)
+ .build());
// Upload testing bytes.
final ParcelFileDescriptor descriptor = mPipeManager.writeDocument(
@@ -69,14 +72,14 @@
// Check if the placeholder file is removed.
try {
- final MtpDocument placeholderDocument = mtpManager.getDocument(0, 1);
+ final MtpObjectInfo placeholderDocument = mtpManager.getObjectInfo(0, 1);
fail(); // The placeholder file has not been deleted.
} catch (IOException e) {
// Expected error, as the file is gone.
}
// Confirm that the target file is created.
- final MtpDocument targetDocument = mtpManager.getDocument(
+ final MtpObjectInfo targetDocument = mtpManager.getObjectInfo(
0, TestMtpManager.CREATED_DOCUMENT_HANDLE);
assertTrue(targetDocument != null);
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 94b5ba0..5605388 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -40,10 +40,9 @@
private final Set<Integer> mValidDevices = new HashSet<>();
private final Set<Integer> mOpenedDevices = new TreeSet<>();
private final Map<Integer, MtpRoot[]> mRoots = new HashMap<>();
- private final Map<String, MtpDocument> mDocuments = new HashMap<>();
+ private final Map<String, MtpObjectInfo> mObjectInfos = new HashMap<>();
private final Map<String, int[]> mObjectHandles = new HashMap<>();
private final Map<String, byte[]> mThumbnailBytes = new HashMap<>();
- private final Map<String, Integer> mParents = new HashMap<>();
private final Map<String, byte[]> mImportFileBytes = new HashMap<>();
TestMtpManager(Context context) {
@@ -54,16 +53,16 @@
mValidDevices.add(deviceId);
}
- void setObjectHandles(int deviceId, int storageId, int objectHandle, int[] documents) {
- mObjectHandles.put(pack(deviceId, storageId, objectHandle), documents);
+ void setObjectHandles(int deviceId, int storageId, int parentHandle, int[] objectHandles) {
+ mObjectHandles.put(pack(deviceId, storageId, parentHandle), objectHandles);
}
void setRoots(int deviceId, MtpRoot[] roots) {
mRoots.put(deviceId, roots);
}
- void setDocument(int deviceId, int objectHandle, MtpDocument document) {
- mDocuments.put(pack(deviceId, objectHandle), document);
+ void setObjectInfo(int deviceId, MtpObjectInfo objectInfo) {
+ mObjectInfos.put(pack(deviceId, objectInfo.getObjectHandle()), objectInfo);
}
void setImportFileBytes(int deviceId, int objectHandle, byte[] bytes) {
@@ -78,10 +77,6 @@
mThumbnailBytes.put(pack(deviceId, objectHandle), bytes);
}
- void setParent(int deviceId, int objectHandle, int parentObjectHandle) {
- mParents.put(pack(deviceId, objectHandle), parentObjectHandle);
- }
-
@Override
void openDevice(int deviceId) throws IOException {
if (!mValidDevices.contains(deviceId) || mOpenedDevices.contains(deviceId)) {
@@ -108,22 +103,13 @@
}
@Override
- MtpDocument getDocument(int deviceId, int objectHandle) throws IOException {
- final String key = pack(deviceId, objectHandle);
- if (mDocuments.containsKey(key)) {
- return mDocuments.get(key);
- } else {
- throw new IOException("getDocument error: " + key);
- }
- }
-
- @Override
MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
- final MtpDocument document = getDocument(deviceId, objectHandle);
- // It's impossible to set an object id of MtpObjectInfo at this stage. Also,
- // it's hard to get any information from MtpDocument, as it's designed to return them
- // only via cursors. Rework these.
- return new MtpObjectInfo.Builder().build();
+ final String key = pack(deviceId, objectHandle);
+ if (mObjectInfos.containsKey(key)) {
+ return mObjectInfos.get(key);
+ } else {
+ throw new IOException("getObjectInfo error: " + key);
+ }
}
@Override
@@ -151,44 +137,29 @@
}
@Override
- int createDocument(int deviceId, MtpObjectInfo objectInfo) throws IOException {
- // For simplicity, it allows to create only one document, and it always has the hardcoded
- // CREATED_DOCUMENT_HANDLE document handle.
+ int createDocument(int deviceId, MtpObjectInfo objectInfo, ParcelFileDescriptor source)
+ throws IOException {
final String key = pack(deviceId, CREATED_DOCUMENT_HANDLE);
- if (!mDocuments.containsKey(key)) {
- mDocuments.put(key, new MtpDocument(
- CREATED_DOCUMENT_HANDLE,
- objectInfo.getFormat(),
- objectInfo.getName(),
- new Date(objectInfo.getDateModified()),
- objectInfo.getCompressedSize(),
- objectInfo.getThumbCompressedSize(),
- false /* Always writable for testing. */));
- } else {
+ if (mObjectInfos.containsKey(key)) {
throw new IOException();
}
+ mObjectInfos.put(key, objectInfo);
+ if (objectInfo.getFormat() != 0x3001) {
+ try (final ParcelFileDescriptor.AutoCloseInputStream inputStream =
+ new ParcelFileDescriptor.AutoCloseInputStream(source)) {
+ final byte[] buffer = new byte[objectInfo.getCompressedSize()];
+ if (inputStream.read(buffer, 0, objectInfo.getCompressedSize()) !=
+ objectInfo.getCompressedSize()) {
+ throw new IOException();
+ }
+
+ mImportFileBytes.put(pack(deviceId, CREATED_DOCUMENT_HANDLE), buffer);
+ }
+ }
return CREATED_DOCUMENT_HANDLE;
}
@Override
- void sendObject(int deviceId, int objectHandle, int size, ParcelFileDescriptor source)
- throws IOException {
- final String key = pack(deviceId, objectHandle);
- if (!mDocuments.containsKey(key)) {
- throw new IOException();
- }
-
- ParcelFileDescriptor.AutoCloseInputStream inputStream =
- new ParcelFileDescriptor.AutoCloseInputStream(source);
- byte[] buffer = new byte[size];
- if (inputStream.read(buffer, 0, size) != size) {
- throw new IOException();
- }
-
- mImportFileBytes.put(pack(deviceId, objectHandle), buffer);
- }
-
- @Override
byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
final String key = pack(deviceId, objectHandle);
if (mThumbnailBytes.containsKey(key)) {
@@ -201,18 +172,18 @@
@Override
void deleteDocument(int deviceId, int objectHandle) throws IOException {
final String key = pack(deviceId, objectHandle);
- if (mDocuments.containsKey(key)) {
- mDocuments.remove(key);
+ if (mObjectInfos.containsKey(key)) {
+ mObjectInfos.remove(key);
} else {
throw new IOException();
}
}
@Override
- synchronized int getParent(int deviceId, int objectHandle) throws IOException {
+ int getParent(int deviceId, int objectHandle) throws IOException {
final String key = pack(deviceId, objectHandle);
- if (mParents.containsKey(key)) {
- return mParents.get(key);
+ if (mObjectInfos.containsKey(key)) {
+ return mObjectInfos.get(key).getParent();
} else {
throw new IOException();
}
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/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index b0429ef..e4b1ed8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -539,10 +539,10 @@
* Otherwise, allow the connect on UUID change.
*/
if (!mProfiles.isEmpty()
- && ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime()
- || (mConnectAttempted == 0))) {
+ && ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime())) {
connectWithoutResettingTimer(false);
}
+
dispatchAttributesChanged();
}
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/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 5a14967..ee296d9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -109,7 +109,7 @@
static String dbNameForUser(final int userHandle) {
// The owner gets the unadorned db name;
- if (userHandle == UserHandle.USER_OWNER) {
+ if (userHandle == UserHandle.USER_SYSTEM) {
return DATABASE_NAME;
} else {
// Place the database in the user-specific data tree so that it's
@@ -186,8 +186,8 @@
createSecureTable(db);
- // Only create the global table for the singleton 'owner' user
- if (mUserHandle == UserHandle.USER_OWNER) {
+ // Only create the global table for the singleton 'owner/system' user
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
createGlobalTable(db);
}
@@ -1252,7 +1252,7 @@
if (upgradeVersion == 82) {
// Move to per-user settings dbs
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
@@ -1306,7 +1306,7 @@
}
if (upgradeVersion == 84) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1331,7 +1331,7 @@
}
if (upgradeVersion == 85) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
try {
// Fix up the migration, ignoring already-migrated elements, to snap up to
@@ -1348,7 +1348,7 @@
}
if (upgradeVersion == 86) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
try {
String[] settingsToMove = {
@@ -1367,7 +1367,7 @@
}
if (upgradeVersion == 87) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
try {
String[] settingsToMove = {
@@ -1386,7 +1386,7 @@
}
if (upgradeVersion == 88) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
try {
String[] settingsToMove = {
@@ -1432,7 +1432,7 @@
}
if (upgradeVersion == 89) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
try {
String[] prefixesToMove = {
@@ -1452,7 +1452,7 @@
}
if (upgradeVersion == 90) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
try {
String[] systemToGlobal = {
@@ -1485,7 +1485,7 @@
}
if (upgradeVersion == 91) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
try {
// Move ringer mode from system to global settings
@@ -1505,7 +1505,7 @@
try {
stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ " VALUES(?,?);");
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
// consider existing primary users to have made it through user setup
// if the globally-scoped device-provisioned bit is set
// (indicating they already made it through setup as primary)
@@ -1526,7 +1526,7 @@
if (upgradeVersion == 93) {
// Redo this step, since somehow it didn't work the first time for some users
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
try {
// Migrate now-global settings
@@ -1547,7 +1547,7 @@
if (upgradeVersion == 94) {
// Add wireless charging started sound setting
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1565,7 +1565,7 @@
}
if (upgradeVersion == 95) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
try {
String[] settingsToMove = { Settings.Global.BUGREPORT_IN_POWER_MENU };
@@ -1584,7 +1584,7 @@
}
if (upgradeVersion == 97) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1613,7 +1613,7 @@
if (upgradeVersion == 100) {
// note: LOCK_SCREEN_SHOW_NOTIFICATIONS now handled in version 106
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1631,7 +1631,7 @@
}
if (upgradeVersion == 101) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1653,7 +1653,7 @@
try {
// The INSTALL_NON_MARKET_APPS setting is becoming per-user rather
// than device-global.
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
// In the owner user, the global table exists so we can migrate the
// entry from there to the secure table, preserving its value.
String[] globalToSecure = {
@@ -1693,7 +1693,7 @@
}
if (upgradeVersion < 105) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1719,7 +1719,7 @@
+ " VALUES(?,?);");
loadIntegerSetting(stmt, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
R.integer.def_lock_screen_show_notifications);
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
final int oldShow = getIntValueFromTable(db,
TABLE_GLOBAL, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, -1);
if (oldShow >= 0) {
@@ -1741,7 +1741,7 @@
if (upgradeVersion < 107) {
// Add trusted sound setting
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1816,7 +1816,7 @@
if (upgradeVersion < 111) {
// reset ringer mode, so it doesn't force zen mode to follow
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1833,7 +1833,7 @@
}
if (upgradeVersion < 112) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
// When device name was added, we went with Manufacturer + Model, device name should
// actually be Model only.
// Update device name to Model if it wasn't modified by user.
@@ -1874,7 +1874,7 @@
// We skipped 114 to handle a merge conflict with the introduction of theater mode.
if (upgradeVersion < 115) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1892,7 +1892,7 @@
}
if (upgradeVersion < 116) {
- if (mUserHandle == UserHandle.USER_OWNER) {
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -2066,7 +2066,7 @@
LockPatternUtils lpu = new LockPatternUtils(mContext);
List<LockPatternView.Cell> cellPattern =
LockPatternUtils.stringToPattern(lockPattern);
- lpu.saveLockPattern(cellPattern, null, UserHandle.USER_OWNER);
+ lpu.saveLockPattern(cellPattern, null, UserHandle.USER_SYSTEM);
} catch (IllegalArgumentException e) {
// Don't want corrupted lock pattern to hang the reboot process
}
@@ -2343,8 +2343,8 @@
private void loadSettings(SQLiteDatabase db) {
loadSystemSettings(db);
loadSecureSettings(db);
- // The global table only exists for the 'owner' user
- if (mUserHandle == UserHandle.USER_OWNER) {
+ // The global table only exists for the 'owner/system' user
+ if (mUserHandle == UserHandle.USER_SYSTEM) {
loadGlobalSettings(db);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 952b220..1d71346 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -123,7 +123,8 @@
}
if (sBroadcastOnRestore.contains(name)) {
- oldValue = table.lookup(cr, name, UserHandle.USER_OWNER);
+ // TODO: http://b/22388012
+ oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
sendBroadcast = true;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 73971ad..8b1caf9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -18,7 +18,7 @@
import android.Manifest;
import android.app.ActivityManager;
-import android.app.AppOpsManager;
+import android.app.AppGlobals;
import android.app.backup.BackupManager;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -47,6 +48,7 @@
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -208,13 +210,13 @@
private volatile UserManager mUserManager;
// We have to call in the package manager with no lock held,
- private volatile PackageManager mPackageManager;
+ private volatile IPackageManager mPackageManager;
@Override
public boolean onCreate() {
synchronized (mLock) {
- mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
- mPackageManager = getContext().getPackageManager();
+ mUserManager = UserManager.get(getContext());
+ mPackageManager = AppGlobals.getPackageManager();
mSettingsRegistry = new SettingsRegistry();
}
registerBroadcastReceivers();
@@ -496,7 +498,7 @@
}
private void dumpForUser(int userId, PrintWriter pw) {
- if (userId == UserHandle.USER_OWNER) {
+ if (userId == UserHandle.USER_SYSTEM) {
pw.println("GLOBAL SETTINGS (user " + userId + ")");
Cursor globalCursor = getAllGlobalSettings(ALL_COLUMNS);
dumpSettings(globalCursor, pw);
@@ -547,7 +549,7 @@
@Override
public void onReceive(Context context, Intent intent) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
switch (intent.getAction()) {
case Intent.ACTION_USER_REMOVED: {
@@ -584,7 +586,7 @@
synchronized (mLock) {
// Get the settings.
SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
- SettingsRegistry.SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER);
+ SettingsRegistry.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
List<String> names = settingsState.getSettingNamesLocked();
@@ -612,7 +614,7 @@
// Get the value.
synchronized (mLock) {
return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_OWNER, name);
+ UserHandle.USER_SYSTEM, name);
}
}
@@ -656,19 +658,19 @@
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry
.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_OWNER, name, value, getCallingPackage());
+ UserHandle.USER_SYSTEM, name, value, getCallingPackage());
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(
SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_OWNER, name);
+ UserHandle.USER_SYSTEM, name);
}
case MUTATION_OPERATION_UPDATE: {
return mSettingsRegistry
.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_OWNER, name, value, getCallingPackage());
+ UserHandle.USER_SYSTEM, name, value, getCallingPackage());
}
}
}
@@ -903,7 +905,7 @@
}
// Enforce what the calling package can mutate the system settings.
- enforceRestrictedSystemSettingsMutationForCallingPackage(operation, name);
+ enforceRestrictedSystemSettingsMutationForCallingPackage(operation, name, runAsUserId);
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(runAsUserId);
@@ -1001,7 +1003,7 @@
}
private void enforceRestrictedSystemSettingsMutationForCallingPackage(int operation,
- String name) {
+ String name, int userId) {
// System/root/shell can mutate whatever secure settings they want.
final int callingUid = Binder.getCallingUid();
if (callingUid == android.os.Process.SYSTEM_UID
@@ -1019,7 +1021,7 @@
}
// The calling package is already verified.
- PackageInfo packageInfo = getCallingPackageInfoOrThrow();
+ PackageInfo packageInfo = getCallingPackageInfoOrThrow(userId);
// Privileged apps can do whatever they want.
if ((packageInfo.applicationInfo.privateFlags
@@ -1039,7 +1041,7 @@
}
// The calling package is already verified.
- PackageInfo packageInfo = getCallingPackageInfoOrThrow();
+ PackageInfo packageInfo = getCallingPackageInfoOrThrow(userId);
// Privileged apps can do whatever they want.
if ((packageInfo.applicationInfo.privateFlags &
@@ -1053,17 +1055,17 @@
}
}
- private PackageInfo getCallingPackageInfoOrThrow() {
+ private PackageInfo getCallingPackageInfoOrThrow(int userId) {
try {
- return mPackageManager.getPackageInfo(getCallingPackage(), 0);
- } catch (PackageManager.NameNotFoundException e) {
+ return mPackageManager.getPackageInfo(getCallingPackage(), 0, userId);
+ } catch (RemoteException e) {
throw new IllegalStateException("Calling package doesn't exist");
}
}
private int getGroupParentLocked(int userId) {
// Most frequent use case.
- if (userId == UserHandle.USER_OWNER) {
+ if (userId == UserHandle.USER_SYSTEM) {
return userId;
}
// We are in the same process with the user manager and the returned
@@ -1401,8 +1403,8 @@
migrateLegacySettingsForUserIfNeededLocked(userId);
// Ensure global settings loaded if owner.
- if (userId == UserHandle.USER_OWNER) {
- final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER);
+ if (userId == UserHandle.USER_SYSTEM) {
+ final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
ensureSettingsStateLocked(globalKey);
}
@@ -1541,7 +1543,7 @@
private void migrateAllLegacySettingsIfNeeded() {
synchronized (mLock) {
- final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER);
+ final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
File globalFile = getSettingsFile(key);
if (globalFile.exists()) {
return;
@@ -1591,7 +1593,7 @@
private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper,
SQLiteDatabase database, int userId) {
// Move over the global settings if owner.
- if (userId == UserHandle.USER_OWNER) {
+ if (userId == UserHandle.USER_SYSTEM) {
final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, userId);
ensureSettingsStateLocked(globalKey);
SettingsState globalSettings = mSettingsStates.get(globalKey);
@@ -1898,7 +1900,7 @@
}
// Set the global settings version if owner.
- if (mUserId == UserHandle.USER_OWNER) {
+ if (mUserId == UserHandle.USER_SYSTEM) {
SettingsState globalSettings = getSettingsLocked(
SettingsRegistry.SETTINGS_TYPE_GLOBAL, mUserId);
globalSettings.setVersionLocked(newVersion);
@@ -1914,7 +1916,7 @@
}
private SettingsState getGlobalSettingsLocked() {
- return getSettingsLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER);
+ return getSettingsLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
}
private SettingsState getSecureSettingsLocked(int userId) {
@@ -1960,7 +1962,7 @@
// v119: Reset zen + ringer mode.
if (currentVersion == 118) {
- if (userId == UserHandle.USER_OWNER) {
+ if (userId == UserHandle.USER_SYSTEM) {
final SettingsState globalSettings = getGlobalSettingsLocked();
globalSettings.updateSettingLocked(Settings.Global.ZEN_MODE,
Integer.toString(Settings.Global.ZEN_MODE_OFF),
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java
index c7cc89b..8e56f47 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java
@@ -48,7 +48,7 @@
Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE
};
- protected int mSecondaryUserId = UserHandle.USER_OWNER;
+ protected int mSecondaryUserId = UserHandle.USER_SYSTEM;
@Override
public void setContext(Context context) {
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderPerformanceTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderPerformanceTest.java
index d581f3b..a09d5fe 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderPerformanceTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderPerformanceTest.java
@@ -47,7 +47,7 @@
// Make sure the setting changed.
String firstValue = getStringViaFrontEndApiSetting(SETTING_TYPE_GLOBAL,
- FAKE_SETTING_NAME, UserHandle.USER_OWNER);
+ FAKE_SETTING_NAME, UserHandle.USER_SYSTEM);
assertEquals("Setting value didn't change", FAKE_SETTING_VALUE, firstValue);
// Set the setting to its second value.
@@ -56,7 +56,7 @@
// Make sure the setting changed.
String secondValue = getStringViaFrontEndApiSetting(SETTING_TYPE_GLOBAL,
- FAKE_SETTING_NAME, UserHandle.USER_OWNER);
+ FAKE_SETTING_NAME, UserHandle.USER_SYSTEM);
assertEquals("Setting value didn't change", FAKE_SETTING_VALUE_1, secondValue);
}
} finally {
@@ -86,20 +86,20 @@
for (int i = 0; i < ITERATION_COUNT; i++) {
// Set the setting to its first value.
setStringViaFrontEndApiSetting(SETTING_TYPE_GLOBAL, FAKE_SETTING_NAME,
- FAKE_SETTING_VALUE, UserHandle.USER_OWNER);
+ FAKE_SETTING_VALUE, UserHandle.USER_SYSTEM);
// Make sure the setting changed.
String firstValue = getStringViaFrontEndApiSetting(SETTING_TYPE_GLOBAL,
- FAKE_SETTING_NAME, UserHandle.USER_OWNER);
+ FAKE_SETTING_NAME, UserHandle.USER_SYSTEM);
assertEquals("Setting value didn't change", FAKE_SETTING_VALUE, firstValue);
// Set the setting to its second value.
setStringViaFrontEndApiSetting(SETTING_TYPE_GLOBAL, FAKE_SETTING_NAME,
- FAKE_SETTING_VALUE_1, UserHandle.USER_OWNER);
+ FAKE_SETTING_VALUE_1, UserHandle.USER_SYSTEM);
// Make sure the setting changed.
String secondValue = getStringViaFrontEndApiSetting(SETTING_TYPE_GLOBAL,
- FAKE_SETTING_NAME, UserHandle.USER_OWNER);
+ FAKE_SETTING_NAME, UserHandle.USER_SYSTEM);
assertEquals("Setting value didn't change", FAKE_SETTING_VALUE_1, secondValue);
}
} finally {
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index ad56b9d..8ca1b46 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -46,40 +46,40 @@
private final Object mLock = new Object();
- public void testSetAndGetGlobalViaFrontEndApiForOwnerUser() throws Exception {
- performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_GLOBAL, UserHandle.USER_OWNER);
+ public void testSetAndGetGlobalViaFrontEndApiForSystemUser() throws Exception {
+ performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
}
- public void testSetAndGetGlobalViaFrontEndApiForNonOwnerUser() throws Exception {
- if (mSecondaryUserId == UserHandle.USER_OWNER) {
+ public void testSetAndGetGlobalViaFrontEndApiForNonSystemUser() throws Exception {
+ if (mSecondaryUserId == UserHandle.USER_SYSTEM) {
Log.w(LOG_TAG, "No secondary user. Skipping "
- + "testSetAndGetGlobalViaFrontEndApiForNonOwnerUser");
+ + "testSetAndGetGlobalViaFrontEndApiForNonSystemUser");
return;
}
performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_GLOBAL, mSecondaryUserId);
}
- public void testSetAndGetSecureViaFrontEndApiForOwnerUser() throws Exception {
- performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SECURE, UserHandle.USER_OWNER);
+ public void testSetAndGetSecureViaFrontEndApiForSystemUser() throws Exception {
+ performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SECURE, UserHandle.USER_SYSTEM);
}
- public void testSetAndGetSecureViaFrontEndApiForNonOwnerUser() throws Exception {
- if (mSecondaryUserId == UserHandle.USER_OWNER) {
+ public void testSetAndGetSecureViaFrontEndApiForNonSystemUser() throws Exception {
+ if (mSecondaryUserId == UserHandle.USER_SYSTEM) {
Log.w(LOG_TAG, "No secondary user. Skipping "
- + "testSetAndGetSecureViaFrontEndApiForNonOwnerUser");
+ + "testSetAndGetSecureViaFrontEndApiForNonSystemUser");
return;
}
performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SECURE, mSecondaryUserId);
}
- public void testSetAndGetSystemViaFrontEndApiForOwnerUser() throws Exception {
- performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SYSTEM, UserHandle.USER_OWNER);
+ public void testSetAndGetSystemViaFrontEndApiForSystemUser() throws Exception {
+ performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SYSTEM, UserHandle.USER_SYSTEM);
}
- public void testSetAndGetSystemViaFrontEndApiForNonOwnerUser() throws Exception {
- if (mSecondaryUserId == UserHandle.USER_OWNER) {
+ public void testSetAndGetSystemViaFrontEndApiForNonSystemUser() throws Exception {
+ if (mSecondaryUserId == UserHandle.USER_SYSTEM) {
Log.w(LOG_TAG, "No secondary user. Skipping "
- + "testSetAndGetSystemViaFrontEndApiForNonOwnerUser");
+ + "testSetAndGetSystemViaFrontEndApiForNonSystemUser");
return;
}
performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SYSTEM, mSecondaryUserId);
@@ -357,7 +357,7 @@
public void run() {
insertStringViaProviderApi(type, name, value, withTableRowUri);
}
- }, type, name, value, UserHandle.USER_OWNER);
+ }, type, name, value, UserHandle.USER_SYSTEM);
}
private void setSettingAndAssertSuccessfulChange(Runnable setCommand, final int type,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e00b55e..05591cc 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -39,6 +39,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<!-- System tool permissions granted to the shell. -->
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
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/res/anim/fab_elevation.xml b/packages/SystemUI/res/anim/fab_elevation.xml
new file mode 100644
index 0000000..2c76a86
--- /dev/null
+++ b/packages/SystemUI/res/anim/fab_elevation.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="true" android:state_pressed="true">
+ <set>
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="translationZ"
+ android:valueTo="@dimen/fab_press_translation_z"
+ android:valueType="floatType" />
+ </set>
+ </item>
+ <item>
+ <set>
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="translationZ"
+ android:valueTo="0"
+ android:valueType="floatType" />
+ </set>
+ </item>
+</selector>
diff --git a/packages/SystemUI/res/drawable-anydpi/nav_app_divider.xml b/packages/SystemUI/res/drawable-anydpi/nav_app_divider.xml
deleted file mode 100644
index b2d988e..0000000
--- a/packages/SystemUI/res/drawable-anydpi/nav_app_divider.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="1dp"
- android:height="24dp"
- android:viewportWidth="1"
- android:viewportHeight="24">
- <path
- android:pathData="M0,0 L1,0 L1,24 L0,24 z"
- android:fillColor="#AAFFFFFF"/>
-</vector>
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/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml b/packages/SystemUI/res/drawable/fab_background.xml
similarity index 67%
copy from core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
copy to packages/SystemUI/res/drawable/fab_background.xml
index 26bc8ad..7f23f2b 100644
--- a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
+++ b/packages/SystemUI/res/drawable/fab_background.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 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,7 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<pathInterpolator
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0.0,0.0 c 0.33333333,0.0 0.0,1.0 1.0,1.0" />
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/fab_ripple">
+ <item>
+ <shape>
+ <solid android:color="@color/fab_shape" />
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/ic_add.xml b/packages/SystemUI/res/drawable/ic_add.xml
new file mode 100644
index 0000000..c573592
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_add.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="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_close_white.xml b/packages/SystemUI/res/drawable/ic_close_white.xml
new file mode 100644
index 0000000..ce64047
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_close_white.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:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_circle.xml b/packages/SystemUI/res/drawable/ic_qs_circle.xml
new file mode 100644
index 0000000..57223cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_circle.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="64dp"
+ android:height="64dp"
+ android:viewportWidth="2.2"
+ android:viewportHeight="2.2">
+
+ <path
+ android:strokeColor="#4DFFFFFF"
+ android:strokeWidth=".05"
+ android:pathData="M.1,1.1
+ c0,.55 .45,1 1,1
+ c.55,0 1,-.45 1,-1
+ c0,-.55 -.45,-1 -1,-1
+ c-.55,0 -1,.45 -1,1"/>
+</vector>
diff --git a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml b/packages/SystemUI/res/layout/horizontal_divider.xml
similarity index 67%
copy from core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
copy to packages/SystemUI/res/layout/horizontal_divider.xml
index 26bc8ad..a060f08 100644
--- a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
+++ b/packages/SystemUI/res/layout/horizontal_divider.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 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,7 +14,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<pathInterpolator
+<View
xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0.0,0.0 c 0.33333333,0.0 0.0,1.0 1.0,1.0" />
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:layout_marginStart="40dp"
+ android:layout_marginEnd="40dp"
+ android:background="#4dffffff" />
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/navigation_bar_with_apps.xml b/packages/SystemUI/res/layout/navigation_bar_with_apps.xml
index 01c239e..ac95b5e 100644
--- a/packages/SystemUI/res/layout/navigation_bar_with_apps.xml
+++ b/packages/SystemUI/res/layout/navigation_bar_with_apps.xml
@@ -82,21 +82,6 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
/>
-
- <ImageView android:id="@+id/app_divider"
- android:focusable="false"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="10dp"
- android:layout_marginRight="10dp"
- android:src="@drawable/nav_app_divider"
- />
-
- <com.android.systemui.statusbar.phone.NavigationBarRecents
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- />
</LinearLayout>
<FrameLayout
@@ -248,21 +233,6 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
/>
-
- <ImageView android:id="@+id/app_divider"
- android:focusable="false"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="10dp"
- android:layout_marginRight="10dp"
- android:src="@drawable/nav_app_divider"
- />
-
- <com.android.systemui.statusbar.phone.NavigationBarRecents
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- />
</LinearLayout>
<FrameLayout
diff --git a/packages/SystemUI/res/layout/qs_customize_layout.xml b/packages/SystemUI/res/layout/qs_customize_layout.xml
new file mode 100644
index 0000000..91cf894
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_customize_layout.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.
+-->
+<com.android.systemui.qs.customize.NonPagedTileLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tiles_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <com.android.systemui.qs.QuickTileLayout
+ android:id="@+id/quick_tile_layout"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_quick_actions_height"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/qs_quick_actions_padding"
+ android:paddingEnd="@dimen/qs_quick_actions_padding" />
+
+ <view
+ class="com.android.systemui.qs.PagedTileLayout$TilePage"
+ android:id="@+id/tile_page"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</com.android.systemui.qs.customize.NonPagedTileLayout>
+
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
new file mode 100644
index 0000000..dc928c7
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -0,0 +1,105 @@
+<?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.
+-->
+<com.android.systemui.qs.customize.QSCustomizer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/navigation_bar_size"
+ android:background="?android:attr/windowBackground">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorPrimary">
+
+ <LinearLayout
+ android:id="@+id/drag_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent"
+ android:orientation="horizontal">
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+ <com.android.systemui.qs.customize.DropButton
+ android:id="@+id/info_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:drawableStart="@drawable/ic_info"
+ android:drawablePadding="10dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@android:color/white"
+ android:text="@string/qs_customize_info" />
+ </FrameLayout>
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+ <com.android.systemui.qs.customize.DropButton
+ android:id="@+id/remove_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:drawableStart="@drawable/ic_close_white"
+ android:drawablePadding="10dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@android:color/white"
+ android:text="@string/qs_customize_remove" />
+ </FrameLayout>
+ </LinearLayout>
+
+ <Toolbar
+ android:id="@*android:id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:navigationContentDescription="@*android:string/action_bar_up_description"
+ style="?android:attr/toolbarStyle"
+ android:background="?android:attr/colorPrimary" />
+ </FrameLayout>
+
+ <com.android.systemui.tuner.AutoScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:elevation="2dp">
+
+ <com.android.systemui.qs.customize.CustomQSPanel
+ android:id="@+id/quick_settings_panel"
+ android:background="#0000"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </com.android.systemui.tuner.AutoScrollView>
+
+ <com.android.systemui.qs.customize.FloatingActionButton
+ android:id="@+id/fab"
+ android:clickable="true"
+ android:layout_width="@dimen/fab_size"
+ android:layout_height="@dimen/fab_size"
+ android:layout_gravity="bottom|end"
+ android:layout_marginEnd="@dimen/fab_margin"
+ android:layout_marginBottom="@dimen/fab_margin"
+ android:elevation="@dimen/fab_elevation"
+ android:background="@drawable/fab_background" />
+
+</com.android.systemui.qs.customize.QSCustomizer>
diff --git a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml b/packages/SystemUI/res/layout/qs_paged_page.xml
similarity index 73%
copy from core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
copy to packages/SystemUI/res/layout/qs_paged_page.xml
index 26bc8ad..eef08ba 100644
--- a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 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,7 +14,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<pathInterpolator
+<view
+ class="com.android.systemui.qs.PagedTileLayout$TilePage"
xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0.0,0.0 c 0.33333333,0.0 0.0,1.0 1.0,1.0" />
+ android:id="@+id/tile_page"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
new file mode 100644
index 0000000..6c236ea
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.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.
+-->
+
+<com.android.systemui.qs.PagedTileLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <view
+ class="com.android.systemui.qs.PagedTileLayout$FirstPage"
+ android:id="@+id/first_page"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <com.android.systemui.qs.QuickTileLayout
+ android:id="@+id/quick_tile_layout"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_quick_actions_height"
+ android:orientation="horizontal"
+ android:paddingLeft="@dimen/qs_quick_actions_padding"
+ android:paddingRight="@dimen/qs_quick_actions_padding" />
+
+ <view
+ class="com.android.systemui.qs.PagedTileLayout$TilePage"
+ android:id="@+id/tile_page"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </view>
+
+ <com.android.systemui.qs.PageIndicator
+ android:id="@+id/page_indicator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:gravity="center" />
+
+</com.android.systemui.qs.PagedTileLayout>
diff --git a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml b/packages/SystemUI/res/layout/qs_tile_layout.xml
similarity index 78%
rename from core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
rename to packages/SystemUI/res/layout/qs_tile_layout.xml
index 26bc8ad..b5d1a1e 100644
--- a/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
+++ b/packages/SystemUI/res/layout/qs_tile_layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 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.
@@ -15,6 +15,7 @@
limitations under the License.
-->
-<pathInterpolator
+<com.android.systemui.qs.TileLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0.0,0.0 c 0.33333333,0.0 0.0,1.0 1.0,1.0" />
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent" />
diff --git a/packages/SystemUI/res/layout/shelf_menu_anchor.xml b/packages/SystemUI/res/layout/shelf_menu_anchor.xml
new file mode 100644
index 0000000..984f655
--- /dev/null
+++ b/packages/SystemUI/res/layout/shelf_menu_anchor.xml
@@ -0,0 +1,24 @@
+<?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="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-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 9294a16..1473f24 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Program is nie op jou toestel geïnstalleer nie"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Wys horlosiesekondes"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Wys horlosiesekondes op die statusbalk. Sal batterylewe dalk beïnvloed."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Herrangskik Kitsinstellings"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Wys helderheid in Kitsinstellings"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Gebruik verdeling in Kitsinstellings"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimenteel"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 13f768d..7ab6af9 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"መተግበሪያ በእርስዎ መሣሪያ ላይ አልተጫነም"</string>
<string name="clock_seconds" msgid="7689554147579179507">"የሰዓት ሰከንዶችን አሳይ"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"የሰዓት ሰከንዶችን በሁኔታ አሞሌ ውስጥ አሳይ። በባትሪ ዕድሜ ላይ ተጽዕኖ ሊኖርው ይችል ይሆናል።"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"ፈጣን ቅንብሮችን ዳግም ያደራጁ"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"በፈጣን ቅንብሮች ውስጥ ብሩህነትን አሳይ"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"በፈጣን ቅንብሮች ውስጥ ምልክት መጥሪያን ይጠቀሙ"</string>
+ <string name="experimental" msgid="6198182315536726162">"የሙከራ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 6889a43..bd02650 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -439,4 +439,8 @@
<string name="activity_not_found" msgid="348423244327799974">"التطبيق غير مثبّت على جهازك"</string>
<string name="clock_seconds" msgid="7689554147579179507">"عرض ثواني الساعة"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"عرض ثواني الساعة في شريط الحالة. قد يؤثر ذلك في عمر البطارية."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"إعادة ترتيب الإعدادات السريعة"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"عرض السطوع في الإعدادات السريعة"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"استخدام ترقيم الصفحات في الإعدادات السريعة"</string>
+ <string name="experimental" msgid="6198182315536726162">"إعدادات تجريبية"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index 51513bd..a0d7d2b 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Tətbiq cihazınızda quraşdırılmayıb"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Saatın saniyəsini göstərin"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Saatın saniyəsini status panelində göstərin. Batareyaya təsir edə bilər."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Sürətli Ayarları yenidən tənzimləyin"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Sürətli ayarlarda parlaqlılığı göstərin"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Sürətli Ayarlarda səhifə nömrələməsindən istifadə edin"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 014d802..a14d0fc 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Приложението не е инсталирано на устройството ви"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Показване на секундите на часовника"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Показване на секундите на часовника в лентата на състоянието. Може да се отрази на живота на батерията."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Пренареждане на бързите настройки"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Показване на яркостта от бързите настройки"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Използване на разделянето на страници от бързите настройки"</string>
+ <string name="experimental" msgid="6198182315536726162">"Експериментални"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 0860019..e819d54 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"আপনার ডিভাইসে অ্যাপ্লিকেশান ইনস্টল করা নেই"</string>
<string name="clock_seconds" msgid="7689554147579179507">"ঘড়ির সেকেন্ড দেখায়"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"স্থিতি দন্ডে ঘড়ির সেকেন্ড দেখায়৷ ব্যাটারি লাইফকে প্রভাবিত করতে পারে৷"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"দ্রুত সেটিংসে পুনরায় সাজান"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"দ্রুত সেটিংসে উজ্জ্বলতা দেখান"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"দ্রুত সেটিংসে পেজিং ব্যবহার করুন"</string>
+ <string name="experimental" msgid="6198182315536726162">"পরীক্ষামূলক"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 9c32052..cc37162 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>
@@ -437,4 +437,8 @@
<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>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Torna a ordenar la Configuració ràpida"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostra la brillantor a la Configuració ràpida"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Utilitza la paginació a la Configuració ràpida"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 4d96a01..a73947f 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -439,4 +439,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplikace není v zařízení nainstalována."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Zobrazit sekundovou ručičku"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Na stavovém řádku se bude zobrazovat sekundová ručička. Může být ovlivněna výdrž baterie."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Změnit uspořádání Rychlého nastavení"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Zobrazit jas v Rychlém nastavení"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Použít v Rychlém nastavení stránkování"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimentální"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 7f005a3..6f49187 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Applikationen er ikke installeret på din enhed."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Vis sekunder"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Vis sekunder i statuslinjen. Dette kan påvirke batteriets levetid."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Omarranger Hurtige indstillinger"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i Hurtige indstillinger"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Brug sidelayout i Hurtige indstillinger"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimentel"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 36f3c98..a659077 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Die App ist nicht auf Ihrem Gerät installiert."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Uhrsekunden anzeigen"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Uhrsekunden in der Statusleiste anzeigen. Kann sich auf die Akkulaufzeit auswirken."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Schnelleinstellungen neu anordnen"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Helligkeit in den Schnelleinstellungen anzeigen"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Seitenlayout in den Schnelleinstellungen verwenden"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimentell"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 8e73b21..bcfc458 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Η εφαρμογή δεν έχει εγκατασταθεί στη συσκευή σας"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Εμφάνιση δευτερολέπτων ρολογιού"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Εμφάνιση δευτερολέπτων ρολογιού στη γραμμή κατάστασης. Ενδέχεται να επηρεάσει τη διάρκεια ζωής της μπαταρίας."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Αναδιάταξη Γρήγορων ρυθμίσεων"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Εμφάνιση φωτεινότητας στις Γρήγορες ρυθμίσεις"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Χρήση τηλεειδοποίησης στις Γρήγορες ρυθμίσεις"</string>
+ <string name="experimental" msgid="6198182315536726162">"Σε πειραματικό στάδιο"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 0b49955..fb781d4 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Application is not installed on your device"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Show clock seconds"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Use paging in Quick Settings"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 0b49955..fb781d4 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Application is not installed on your device"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Show clock seconds"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Use paging in Quick Settings"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 0b49955..fb781d4 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Application is not installed on your device"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Show clock seconds"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Use paging in Quick Settings"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index c4dca9c..10834a2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"La aplicación no está instalada en el dispositivo"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostrar los segundos del reloj"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Muestra los segundos del reloj en la barra de estado. Puede afectar la duración de la batería."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar la Configuración rápida"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostrar el brillo en la Configuración rápida"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Usar la paginación en la Configuración rápida"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index e316a94..37c81f4 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"La aplicación no está instalada en tu dispositivo"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostrar los segundos del reloj"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Muestra los segundos del reloj en la barra de estado. Puede afectar a la duración de la batería."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar Ajustes rápidos"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostrar brillo en Ajustes rápidos"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Utilizar paginación en Ajustes rápidos"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index a151bce..f4218a3 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Rakendust pole teie seadmesse installitud"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Kella sekundite kuvamine"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Olekuribal kella sekundite kuvamine. See võib mõjutada aku kasutusaega."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Korralda kiirseaded ümber"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Kuva kiirseadetes heledus"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Kasuta kiirseadetes lehe paigutust"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimentaalne"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 37cdee7..0e5532c 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplikazioa ez dago gailuan instalatuta"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Erakutsi erlojuko segundoak"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Erakutsi erlojuko segundoak egoera-barran. Baliteke bateria gehiago erabiltzea."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Berrantolatu ezarpen bizkorrak"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Erakutsi distira Ezarpen bizkorretan"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Erabili orriak pasatzeko diseinu berria Ezarpen bizkorretan"</string>
+ <string name="experimental" msgid="6198182315536726162">"Esperimentala"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 80d0918..7593f46 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>
@@ -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>
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"برنامه در دستگاه شما نصب نیست"</string>
<string name="clock_seconds" msgid="7689554147579179507">"نمایش ثانیههای ساعت"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"ثانیههای ساعت را در نوار وضعیت نشان میدهد. ممکن است بر ماندگاری باتری تأثیر بگذارد."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"ترتیب مجدد در تنظیمات سریع"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"نمایش روشنایی در تنظیمات سریع"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"استفاده از صفحهبندی در تنظیمات سریع"</string>
+ <string name="experimental" msgid="6198182315536726162">"آزمایشی"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index b86247a..6c4a029 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Sovellusta ei ole asennettu laitteellesi."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Näytä sekunnit kellossa"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Näytä sekunnit tilapalkin kellossa. Tämä voi vaikuttaa akun kestoon."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Järjestä pika-asetukset uudelleen"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Näytä kirkkaus pika-asetuksissa"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Käytä sivutusta pika-asetuksissa"</string>
+ <string name="experimental" msgid="6198182315536726162">"Kokeellinen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index f99447c..07a2fc3 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"L\'application n\'est pas installée sur votre appareil"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Afficher les secondes sur l\'horloge"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes sur l\'horloge dans la barre d\'état. Cela peut réduire l\'autonomie de la pile."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Réorganiser les paramètres rapides"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Afficher la luminosité dans les paramètres rapides"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Utiliser la pagination dans les paramètres rapides"</string>
+ <string name="experimental" msgid="6198182315536726162">"Fonctions expérimentales"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index aada1eb..0aa45ae 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"L\'application n\'est pas installée sur votre appareil."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Afficher les secondes sur l\'horloge"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes dans la barre d\'état. Cela risque de réduire l\'autonomie de la batterie."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Réorganiser la fenêtre de configuration rapide"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Afficher la luminosité dans fenêtre de configuration rapide"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Utiliser mise en page dans fenêtre de configuration rapide"</string>
+ <string name="experimental" msgid="6198182315536726162">"Paramètres expérimentaux"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 951809f..ec64f22 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"A aplicación non está instalada no teu dispositivo"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostrar segundos do reloxo"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra os segundos do reloxo na barra de estado. Pode influír na duración da batería."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar Configuración rápida"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostrar brillo en Configuración rápida"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Utilizar paxinación en Configuración rápida"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 02e4e52..e873c95 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"તમારા ઉપકરણ પર એપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી"</string>
<string name="clock_seconds" msgid="7689554147579179507">"ઘડિયાળ સેકન્ડ બતાવો"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"ઘડિયાળ સેકન્ડ સ્થિતિ બારમાં બતાવો. બૅટરીની આવરદા પર અસર કરી શકે છે."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"ઝડપી સેટિંગ્સને ફરીથી ગોઠવો"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"ઝડપી સેટિંગ્સમાં તેજ બતાવો"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"ઝડપી સેટિંગ્સમાં પૃષ્ઠાંકનનો ઉપયોગ કરો"</string>
+ <string name="experimental" msgid="6198182315536726162">"પ્રાયોગિક"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 3ed090c..83ac46e 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"ऐप्लिकेशन आपके डिवाइस पर इंस्टॉल नहीं है"</string>
<string name="clock_seconds" msgid="7689554147579179507">"घड़ी के सेकंड दिखाएं"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"स्थिति बार में घड़ी के सेकंड दिखाएं. इससे बैटरी के जीवनकाल पर प्रभाव पड़ सकता है."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"त्वरित सेटिंग को पुन: व्यवस्थित करें"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"त्वरित सेटिंग में स्क्रीन की रोशनी दिखाएं"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"त्वरित सेटिंग में पेजिंग का उपयोग करें"</string>
+ <string name="experimental" msgid="6198182315536726162">"प्रयोगात्मक"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9698aba..b5d9931 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -436,4 +436,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplikacija nije instalirana na vašem uređaju"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Prikaži sekunde na satu"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Prikazuju se sekunde na satu na traci statusa. Može utjecati na trajanje baterije."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Promijeni raspored Brzih postavki"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Prikaži svjetlinu u Brzim postavkama"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Upotrijebi redni broj stranice u Brzim postavkama"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimentalno"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 37cb0a8..79390a6 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Az alkalmazás nincs telepítve eszközén."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Másodpercek megjelenítése az órán"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Másodpercek megjelenítése az állapotsor óráján. Ez hatással lehet az akkumulátor üzemidejére."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Gyorsbeállítások átrendezése"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Fényerő megjelenítése a gyorsbeállításokban"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Oldalelrendezés használata a gyorsbeállításokban"</string>
+ <string name="experimental" msgid="6198182315536726162">"Kísérleti"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index e31cf49..9f48b26 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Հավելվածը տեղադրված չէ սարքի վրա"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Ցույց տալ ժամացույցի վայրկյանները"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Ցույց տալ ժամացույցի վայրկյանները կարգավիճակի տողում: Կարող է ազդել մարտկոցի աշխատանքի ժամանակի վրա:"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Վերադասավորել Արագ կարգավորումները"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Ցույց տալ պայծառությունն Արագ կարգավորումներում"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Օգտագործել էջերի դասավորությունը Արագ կարգավորումներում"</string>
+ <string name="experimental" msgid="6198182315536726162">"Փորձնական"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index ad8141b..60024115 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplikasi tidak dipasang di perangkat"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Tampilkan detik jam"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Tampilkan detik jam di bilah status. Dapat memengaruhi masa pakai baterai."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Atur Ulang Setelan Cepat"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Tampilkan kecerahan di Setelan Cepat"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Gunakan pembagian laman di Setelan Cepat"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 02caf17..e8e0530 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Forritið er ekki uppsett í tækinu."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Sýna sekúndur á klukku"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Sýna sekúndur á klukku í stöðustikunni. Getur haft áhrif á endingu rafhlöðu."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Endurraða flýtistillingum"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Sýna birtustig í flýtistillingum"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Nota síðuskoðun í flýtistillingum"</string>
+ <string name="experimental" msgid="6198182315536726162">"Tilraunastillingar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index ab5d8beab..8ee5f07 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Applicazione non installata sul dispositivo"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostra i secondi nell\'orologio"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra i secondi nell\'orologio nella barra di stato. Ciò potrebbe ridurre la durata della carica della batteria."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Riorganizza Impostazioni rapide"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostra luminosità in Impostazioni rapide"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Utilizza nuovo layout in Impostazioni rapide"</string>
+ <string name="experimental" msgid="6198182315536726162">"Sperimentali"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 9dad8aa..44058a3 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"האפליקציה אינה מותקנת במכשיר"</string>
<string name="clock_seconds" msgid="7689554147579179507">"הצג שניות בשעון"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"הצג שניות בשעון בשורת הסטטוס. פעולה זו עשויה להשפיע על אורך חיי הסוללה."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"סידור מחדש של הגדרות מהירות"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"הצג בהירות בהגדרות מהירות"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"השתמש באפשרות הדפדוף בהגדרות המהירות"</string>
+ <string name="experimental" msgid="6198182315536726162">"ניסיוניות"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 9cb2fc8..a73f4c1 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"アプリが端末にインストールされていません"</string>
<string name="clock_seconds" msgid="7689554147579179507">"時計の秒を表示"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"ステータスバーに時計の秒を表示します。電池使用量に影響する可能性があります。"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"クイック設定を並べ替え"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"クイック設定に明るさ調整バーを表示する"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"クイック設定でページ設定を使用する"</string>
+ <string name="experimental" msgid="6198182315536726162">"試験運用版"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index a9728b2..f1035a8 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"აპლიკაცია თქვენს მოწყობილობაზე დაყენებული არ არის"</string>
<string name="clock_seconds" msgid="7689554147579179507">"საათის წამების ჩვენება"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"საათის წამების ჩვენება სტატუსის ზოლში. შეიძლება გავლენა იქონიოს ბატარეაზე."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"სწრაფი პარამეტრების გადაწყობა"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"სიკაშკაშის ჩვენება სწრაფ პარამეტრებში"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"გამოიყენეთ გვერდების დანომვრა სწრაფ პარამეტრებში"</string>
+ <string name="experimental" msgid="6198182315536726162">"ექსპერიმენტული"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 1d61372..8b82e5f 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Қолданба құрылғыда орнатылмаған"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Сағат секундтарын көрсету"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Күйін көрсету жолағында сағат секундтарын көрсету. Батареяның қызмет көрсету мерзіміне әсер етуі мүмкін."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Жылдам параметрлерді қайта реттеу"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Жылдам параметрлерде жарықтықты көрсету"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Жылдам параметрлерде беттерді нөмірлеуді пайдалану"</string>
+ <string name="experimental" msgid="6198182315536726162">"Эксперименттік"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index c233f83..b8db00f 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"កម្មវិធីមិនបានដំឡើងនៅលើឧបករណ៍របស់អ្នកទេ"</string>
<string name="clock_seconds" msgid="7689554147579179507">"បង្ហាញវិនាទី"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"បង្ហាញវិនាទីនៅលើរបារស្ថានភាពអាចនឹងប៉ះពាល់ដល់ថាមពលថ្ម។"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"រៀបចំការកំណត់រហ័សឡើងវិញ"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"បង្ហាញកម្រិតពន្លឺនៅក្នុងការកំណត់រហ័ស"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"ប្រើការចុះទំព័រនៅក្នុងការកំណត់រហ័ស"</string>
+ <string name="experimental" msgid="6198182315536726162">"ពិសោធន៍"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index d486e56..316c04f 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"</string>
<string name="clock_seconds" msgid="7689554147579179507">"ಗಡಿಯಾರದ ಸೆಕೆಂಡುಗಳನ್ನು ತೋರಿಸು"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಲ್ಲಿ ಗಡಿಯಾರ ಸೆಕೆಂಡುಗಳನ್ನು ತೋರಿಸು. ಇದಕ್ಕೆ ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯು ಪರಿಣಾಮಬೀರಬಹುದು."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಮರುಹೊಂದಿಸಿ"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಪ್ರಖರತೆಯನ್ನು ತೋರಿಸಿ"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಪುಟಗಳನ್ನು ಬಳಸಿ"</string>
+ <string name="experimental" msgid="6198182315536726162">"ಪ್ರಾಯೋಗಿಕ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 3505754..d7f1543 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"기기에 애플리케이션이 설치되어 있지 않습니다."</string>
<string name="clock_seconds" msgid="7689554147579179507">"시계 초 단위 표시"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"상태 표시줄에 시계 초 단위를 표시합니다. 배터리 수명에 영향을 줄 수도 있습니다."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"빠른 설정 재정렬"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"빠른 설정에서 밝기 표시"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"빠른 설정에서 페이지 레이아웃 사용"</string>
+ <string name="experimental" msgid="6198182315536726162">"베타"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 968a65a..65f91a2 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Колдонмо сиздин түзмөгүңүздө орнотулган эмес"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Сааттын секунддары көрсөтүлсүн"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Абал тилкесинен сааттын секунддары көрсөтүлсүн. Батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Ыкчам жөндөөлөрдү кайра коюу"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Ыкчам жөндөөлөрдөн жарык деңгээлин көрсөтүү"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Ыкчам жөндөөлөрдөн баракты номерлөөнү колдонуу"</string>
+ <string name="experimental" msgid="6198182315536726162">"Сынамык"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index f34c70e..50b4522 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"ແອັບພລິເຄຊັນບໍ່ຖືກຕິດຕັ້ງຢູ່ໃນອຸປະກອນຂອງທ່ານ"</string>
<string name="clock_seconds" msgid="7689554147579179507">"ສະແດງວິນາທີໂມງ"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"ສະແດງວິນາທີໂມງຢູ່ໃນແຖບສະຖານະ. ອາດຈະມີຜົນກະທົບຕໍ່ອາຍຸແບັດເຕີຣີ."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"ຈັດວາງການຕັ້ງຄ່າດ່ວນຄືນໃໝ່"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"ສະແດງຄວາມແຈ້ງຢູ່ໃນການຕັ້ງຄ່າດ່ວນ"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"ໃຊ້ການໃສ່ໜ້າຢູ່ໃນການຕັ້ງຄ່າດ່ວນ"</string>
+ <string name="experimental" msgid="6198182315536726162">"ຍັງຢູ່ໃນການທົດລອງ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index cf70d6b..9082778 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Programa neįdiegta įrenginyje"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Rodyti laikrodžio sekundes"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Rodyti laikrodžio sekundes būsenos juostoje. Tai gali paveikti akumuliatoriaus naudojimo laiką."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Pertvarkyti sparčiuosius nustatymus"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Rodyti skaistį sparčiuosiuose nustatymuose"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Naudoti puslapių kaitą sparčiuosiuose nustatymuose"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimentinė versija"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 5e2db80..5fb4bb0 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -436,4 +436,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Lietojumprogramma nav instalēta jūsu ierīcē."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Rādīt pulksteņa sekundes"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Statusa joslā rādīt pulksteņa sekundes. Var ietekmēt akumulatora darbības laiku."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Pārkārtot ātros iestatījumus"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Rādīt spilgtumu ātrajos iestatījumos"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Izmantot lapošanu ātrajos iestatījumos"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimentāli"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index fe781b6..8571b3f 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Апликацијата не е инсталирана на уредот"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Прикажи ги секундите на часовникот"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Прикажи ги секундите на часовникот на статусната лента. Може да влијае на траењето на батеријата."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Преуредете ги Брзи поставки"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Прикажете ја осветленоста во Брзи поставки"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Користете прелистување страници во Брзи поставки"</string>
+ <string name="experimental" msgid="6198182315536726162">"Експериментално"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 6e6fc55..4b11962 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"നിങ്ങളുടെ ഉപകരണത്തിൽ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാൾ ചെയ്തിട്ടില്ല"</string>
<string name="clock_seconds" msgid="7689554147579179507">"ക്ലോക്ക് സെക്കൻഡ് കാണിക്കുക"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"സ്റ്റാറ്റസ് ബാറിൽ ക്ലോക്ക് സെക്കൻഡ് കാണിക്കുന്നത് ബാറ്ററിയുടെ ലൈഫിനെ ബാധിക്കാം."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"ദ്രുത ക്രമീകരണം പുനഃസജ്ജീകരിക്കുക"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"ദ്രുത ക്രമീകരണത്തിൽ തെളിച്ചം കാണിക്കുക"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"ദ്രുത്ര ക്രമീകരണത്തിൽ പേജിംഗ് ഉപയോഗിക്കുക"</string>
+ <string name="experimental" msgid="6198182315536726162">"പരീക്ഷണാത്മകം!"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 65250bb..d1dbd6d 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -433,4 +433,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Апп-ыг таны төхөөрөмжид суулгаагүй байна"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Цагийн секундыг харуулах"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Статус талбарт цагийн секундыг харуулах. Энэ нь тэжээлийн цэнэгт нөлөөлж болно."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Түргэн тохиргоог дахин засварлах"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Түргэн тохиргоонд гэрэлтүүлэг харах"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Хуудаслалтыг түргэн тохиргоонд ашиглаарай"</string>
+ <string name="experimental" msgid="6198182315536726162">"Туршилтын"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 0f71b4b..2b9d953 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"अनुप्रयोग आपल्या डिव्हाइसवर स्थापित केलेला नाही"</string>
<string name="clock_seconds" msgid="7689554147579179507">"घड्याळ सेकंद दर्शवा"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"स्टेटस बारमध्ये घड्याळ सेकंद दर्शवा. कदाचित बॅटरी आयुष्य प्रभावित होऊ शकते."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"द्रुत सेटिंग्जची पुनर्रचना करा"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"द्रुत सेटिंग्जमध्ये चमक दर्शवा"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"द्रुत सेटिंग्जमध्ये लिखाण वापरा"</string>
+ <string name="experimental" msgid="6198182315536726162">"प्रायोगिक"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 596275c..01f7edf 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplikasi tidak dipasang pada peranti anda"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Tunjukkan saat jam"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Tunjukkan saat jam dalam bar status. Mungkin menjejaskan hayat bateri."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Susun Semula Tetapan Pantas"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Tunjukkan kecerahan dalam Tetapan Pantas"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Gunakan penghalaman dalam Tetapan Pantas"</string>
+ <string name="experimental" msgid="6198182315536726162">"Percubaan"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 3147d0c..d0eaccf 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"အပလီကေးရှင်းကို သင်၏ ကိရိယာထဲသို့ တပ်ဆင်မပေးရသေးပါ။"</string>
<string name="clock_seconds" msgid="7689554147579179507">"နာရီ စက္ကန့်များကို ပြရန်"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"အခြေအနေပြနေရာမှာ နာရီ စက္ကန့်များကို ပြပါ။ ဘက်ထရီ သက်တမ်းကို အကျိုးသက်ရောက်နိုင်တယ်။"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"အမြန် ဆက်တင်များကို ပြန်စီစဉ်ရန်"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"အမြန် ဆက်တင်များထဲက တောက်ပမှုကို ပြရန်"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"အမြန် ဆက်တင်များထဲတွင် စာမျက်နှာ ပုံစံချမှုကို အသုံးပြုပါ"</string>
+ <string name="experimental" msgid="6198182315536726162">"စမ်းသပ်ရေး"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index f1bfbe1..e582039 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Appen er ikke installert på enheten din"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Vis sekunder på klokken"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Vis sekunder i statusfeltet på klokken. Det kan påvirke batteritiden."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Omorganiser hurtiginnstillingene"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i hurtiginnstillingene"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Bruk sidetall i hurtiginnstillingene"</string>
+ <string name="experimental" msgid="6198182315536726162">"På forsøksstadiet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 461a15f..c3dc703 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"तपाईँको यन्त्रमा अनुप्रयोग स्थापना भएको छैन"</string>
<string name="clock_seconds" msgid="7689554147579179507">"घडीमा सेकेन्ड देखाउनुहोस्"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"वस्तुस्थिति पट्टीको घडीमा सेकेन्ड देखाउनुहोस्। ब्याट्री आयु प्रभावित हुन सक्छ।"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"द्रुत सेटिङहरू पुनः व्यवस्थित गर्नुहोस्"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"द्रुत सेटिङहरूमा उज्यालो देखाउनुहोस्"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"द्रुत सेटिङहरूमा पेजिंग प्रयोग गर्नुहोस्"</string>
+ <string name="experimental" msgid="6198182315536726162">"प्रयोगात्मक"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index b74f91d..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,11 @@
<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>
+ <string name="show_brightness" msgid="6613930842805942519">"Helderheid weergeven in Snelle instellingen"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Paginering gebruiken in Snelle instellingen"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimenteel"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index 06746cc..ef3d2f3 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"ਐਪਲੀਕੇਸ਼ਨ ਤੁਹਾਡੀ ਡਿਵਾਈਸ ਤੇ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤੀ ਗਈ ਹੈ"</string>
<string name="clock_seconds" msgid="7689554147579179507">"ਘੜੀ ਸਕਿੰਟ ਦਿਖਾਓ"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"ਸਥਿਤੀ ਬਾਰ ਵਿੱਚ ਘੜੀ ਸਕਿੰਟ ਦਿਖਾਓ। ਬੈਟਰੀ ਸਮਰੱਥਾ ਤੇ ਅਸਰ ਪੈ ਸਕਦਾ ਹੈ।"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਨੂੰ ਦੁਬਾਰਾ ਕ੍ਰਮ ਦਿਓ"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਚਮਕ ਦਿਖਾਓ"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਪੇਜਿੰਗ ਵਰਤੋ"</string>
+ <string name="experimental" msgid="6198182315536726162">"ਪ੍ਰਯੋਗਾਤਮਿਕ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 66100c1..8c2c5c4 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplikacja nie jest zainstalowana na urządzeniu"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Pokaż sekundy na zegarku"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Pokaż sekundy na zegarku na pasku stanu. Może mieć wpływ na czas pracy baterii."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Uporządkuj Szybkie ustawienia"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Pokaż jasność w Szybkich ustawieniach"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Użyj stronicowania w Szybkich ustawieniach"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperymentalne"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 53cea01..3803b81 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"O app não está instalado no seu dispositivo"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostrar segundos do relógio"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de status. Pode afetar a duração da bateria."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar \"Configurações rápidas\""</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostrar brilho nas \"Configurações rápidas\""</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Usar paginação nas \"Configurações rápidas\""</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimentais"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 5006413..12609c7 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"A aplicação não está instalada no dispositivo"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostrar segundos do relógio"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de estado. Pode afetar a autonomia da bateria."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar as Definições rápidas"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostrar luminosidade nas Definições rápidas"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Utilizar paginação nas Definições rápidas"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 53cea01..3803b81 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"O app não está instalado no seu dispositivo"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostrar segundos do relógio"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de status. Pode afetar a duração da bateria."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar \"Configurações rápidas\""</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostrar brilho nas \"Configurações rápidas\""</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Usar paginação nas \"Configurações rápidas\""</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimentais"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index d97e16b..bb8f2c3 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -436,4 +436,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplicația nu este instalată pe dispozitiv"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Afișează secundele pe ceas"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Afișează secundele pe ceas în bara de stare. Poate afecta autonomia bateriei."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Rearanjați Setările rapide"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Afișați luminozitatea în Setările rapide"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Folosiți paginarea în Setările rapide"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimentale"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index d67ad38..9609f40 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -269,7 +269,7 @@
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Нет сети"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi выкл."</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Не удалось найти доступные сети Wi-Fi"</string>
- <string name="quick_settings_cast_title" msgid="7709016546426454729">"Wi-Fi-монитор"</string>
+ <string name="quick_settings_cast_title" msgid="7709016546426454729">"Трансляция"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Передача изображения"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Безымянное устройство"</string>
<string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Готово к передаче"</string>
@@ -439,4 +439,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Приложение не установлено на вашем устройстве"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Показывать секунды"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Показывать в строке состояния время с точностью до секунды (заряд батареи может расходоваться быстрее)."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Изменить порядок Быстрых настроек"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Добавить яркость в Быстрые настройки"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Разбить Быстрые настройки на страницы"</string>
+ <string name="experimental" msgid="6198182315536726162">"Экспериментальная функция"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 06cb626..9e78834 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"යෙදුම ඔබේ උපාංගය මත ස්ථාපනය කර නැත"</string>
<string name="clock_seconds" msgid="7689554147579179507">"ඔරලෝසු තත්පර පෙන්වන්න"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"තත්ත්ව තීරුවෙහි ඔරලෝසු තත්පර පෙන්වන්න. බැටරි ආයු කාලයට බලපෑමට හැකිය."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"ඉක්මන් සැකසීම් යළි පිළිවෙළට සකසන්න"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"ඉක්මන් සැකසීම්වල දීප්තිය පෙන්වන්න"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"ඉක්මන් සැකසීම්වල පිටු පිරිසැලසුම් භාවිත කරන්න"</string>
+ <string name="experimental" msgid="6198182315536726162">"පරීක්ෂණාත්මක"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 037074b..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>
@@ -439,4 +439,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplikácia nie je nainštalovaná na zariadení"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Zobraziť sekundy"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Zobrazí sekundy v stavovom riadku. Môže to ovplyvňovať výdrž batérie."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Zmeniť usporiadanie Rýchlych nastavení"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Zobraziť jas v Rýchlych nastaveniach"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Použite stránkovanie v Rýchlych nastaveniach"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimentálne"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 31fe5d8..4dffcfa 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplikacija ni nameščena v napravi"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Prikaz sekund pri uri"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Prikaže sekunde pri uri v vrstici stanja. To lahko vpliva na čas delovanja pri akumulatorskem napajanju."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Preuredi hitre nastavitve"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Prikaz svetlosti v hitrih nastavitvah"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Uporaba postavitve strani v hitrih nastavitvah"</string>
+ <string name="experimental" msgid="6198182315536726162">"Poskusno"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index ffb8bea..20c3426 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Aplikacioni nuk është instaluar në pajisjen tënde."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Trego sekondat e orës"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Trego sekondat e orës në shiritin e statusit. Mund të ndikojë te jeta e baterisë."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Risistemo Cilësimet e shpejta"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Shfaq ndriçimin te Cilësimet e shpejta"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Përdor faqosjen te Cilësimet e shpejta"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimentale"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index bae0284..1dcde3a 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -436,4 +436,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Апликација није инсталирана на уређају"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Приказуј секунде на сату"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Секунде на сату се приказују на статусној траци. То може да утиче на трајање батерије."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Преуреди Брза подешавања"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Прикажи осветљеност у Брзим подешавањима"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Користи листање страница у Брзим подешавањима"</string>
+ <string name="experimental" msgid="6198182315536726162">"Експериментално"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 6588f10..0abeb1e 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Appen är inte installerad på enheten"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Visa klocksekunder"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Visa klocksekunder i statusfältet. Detta kan påverka batteritiden."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Ordna snabbinställningarna"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Visa ljusstyrka i snabbinställningarna"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Använd sidindelning i snabbinställningarna"</string>
+ <string name="experimental" msgid="6198182315536726162">"Experimentella"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 623f12c..d000004 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Programu haijasakinishwa kwenye kifaa chako"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Onyesha sekunde za saa"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Onyesha sekunde za saa katika sehemu ya arifa. Inaweza kuathiri muda wa matumizi ya betri."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Panga Upya Mipangilio ya Haraka"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Onyesha unga\'avu katika Mipangilio ya Haraka"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Tumia nambari za ukurasa katika Mipangilio ya Haraka"</string>
+ <string name="experimental" msgid="6198182315536726162">"Ya majaribio"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 195fdb1..1af4ab1 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -98,4 +98,6 @@
In sw600dp we want the buttons centered so this fills the space,
(screen_pinning_request_width - 3 * screen_pinning_request_button_width) / 2 -->
<dimen name="screen_pinning_request_side_width">8dp</dimen>
+
+ <dimen name="fab_margin">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index c6cb337..7b1ded3 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"சாதனத்தில் பயன்பாடு நிறுவப்படவில்லை"</string>
<string name="clock_seconds" msgid="7689554147579179507">"கடிகார வினாடிகளைக் காட்டு"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"நிலைப் பட்டியில் கடிகார வினாடிகளைக் காட்டும். பேட்டரியின் ஆயுளைக் குறைக்கலாம்."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"விரைவு அமைப்புகளை மறுவரிசைப்படுத்து"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"விரைவு அமைப்புகளில் ஒளிர்வுப் பட்டியைக் காட்டு"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"விரைவு அமைப்புகளில் புதிய பக்கத் தளவமைப்பைப் பயன்படுத்து"</string>
+ <string name="experimental" msgid="6198182315536726162">"சோதனை முயற்சி"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index c0717c1..06762bd 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"అనువర్తనం మీ పరికరంలో ఇన్స్టాల్ చేయలేదు"</string>
<string name="clock_seconds" msgid="7689554147579179507">"గడియారం సెకన్లు చూపు"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"స్థితి పట్టీలో గడియారం సెకన్లు చూపుతుంది. బ్యాటరీ శక్తి ప్రభావితం చేయవచ్చు."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"శీఘ్ర సెట్టింగ్ల ఏర్పాటు క్రమం మార్చు"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"శీఘ్ర సెట్టింగ్ల్లో ప్రకాశం చూపు"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"శీఘ్ర సెట్టింగ్ల్లో పేజింగ్ను ఉపయోగించు"</string>
+ <string name="experimental" msgid="6198182315536726162">"ప్రయోగాత్మకం"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index e16fdd2..52c7af7 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"ยังไม่ได้ติดตั้งแอปพลิเคชันบนอุปกรณ์ของคุณ"</string>
<string name="clock_seconds" msgid="7689554147579179507">"แสดงวินาทีของนาฬิกา"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"แสดงวินาทีของนาฬิกาในแถบสถานะ อาจส่งผลต่ออายุแบตเตอรี"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"จัดเรียงการตั้งค่าด่วนใหม่"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"แสดงความสว่างในการตั้งค่าด่วน"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"ใช้การแบ่งหน้าในการตั้งค่าด่วน"</string>
+ <string name="experimental" msgid="6198182315536726162">"ทดสอบ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index d970084..66056d1 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Hindi naka-install ang application sa iyong device"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Ipakita ang mga segundo ng orasan"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Ipakita ang mga segundo ng orasan sa status bar. Maaaring makaapekto sa tagal ng baterya."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Ayusing Muli ang Mga Mabilisang Setting"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Ipakita ang liwanag sa Mga Mabilisang Setting"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Gamitin ang paging sa Mga Mabilisang Setting"</string>
+ <string name="experimental" msgid="6198182315536726162">"Pang-eksperimento"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index b22b4fb..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>
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Uygulama, cihazınızda yüklü değil"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Saatin saniyelerini göster"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Durum çubuğunda saatin saniyelerini gösterir. Pil ömrünü etkileyebilir."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Hızlı Ayarlar\'ı Yeniden Düzenle"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Hızlı Ayarlar\'da parlaklığı göster"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Hızlı Ayarlar\'da sayfa ayırmayı kullan"</string>
+ <string name="experimental" msgid="6198182315536726162">"Deneysel"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 898611d..9dde801 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Додаток не встановлено на вашому пристрої"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Показувати секунди на годиннику"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Показувати секунди на годиннику в рядку стану. Акумулятор може розряджатися швидше."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Упорядкувати швидкі налаштування"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Показувати панель яскравості у швидких налаштуваннях"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Показувати швидкі налаштування у вигляді сторінок"</string>
+ <string name="experimental" msgid="6198182315536726162">"Експериментальні налаштування"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index d5683e1..a4ef16a 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"ایپلیکیشن آپ کے آلہ پر انسٹال نہیں ہے"</string>
<string name="clock_seconds" msgid="7689554147579179507">"گھڑی کے سیکنڈز دکھائیں"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"گھڑی کے سیکنڈز اسٹیٹس بار میں دکھائیں۔ اس کا بیٹری کی زندگی پر اثر پڑ سکتا ہے۔"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"فوری ترتیبات کو دوبارہ ترتیب دیں"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"فوری ترتیبات میں چمکیلا پن دکھائیں"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"فوری ترتیبات میں پیجنگ استعمال کریں"</string>
+ <string name="experimental" msgid="6198182315536726162">"تجرباتی"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index e31af6b..04ec78b 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Ilova qurilmangizga o‘rnatilmagan"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Soat soniyalari ko‘rsatilsin"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Holat panelida soat soniyalari ko‘rsatilsin. Bu batareya resursiga ta’sir qilishi mumkin."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Tezkor sozlamalarni qayta tartiblash"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Tezkor sozlamalarda yorqinlikni ko‘rsatish"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Tezkor sozlamalarni sahifalarga ajratish"</string>
+ <string name="experimental" msgid="6198182315536726162">"Tajribaviy"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 030bbbe..460d57a 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Ứng dụng chưa được cài đặt trên thiết bị của bạn"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Hiển thị giây đồng hồ"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Hiển thị giây đồng hồ trong thanh trạng thái. Có thể ảnh hưởng đến thời lượng pin."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Sắp xếp lại Cài đặt nhanh"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Hiển thị độ sáng trong Cài đặt nhanh"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Sử dụng đánh số trang trong Cài đặt nhanh"</string>
+ <string name="experimental" msgid="6198182315536726162">"Thử nghiệm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index b046415..4d70f24 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"您的设备中未安装此应用"</string>
<string name="clock_seconds" msgid="7689554147579179507">"显示时钟的秒数"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"在状态栏中显示时钟的秒数。这可能会影响电池的续航时间。"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速设置"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"在快速设置中显示亮度栏"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"在快速设置中使用分页功能"</string>
+ <string name="experimental" msgid="6198182315536726162">"实验性"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index d568dba..f7cac90 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"尚未在裝置安裝應用程式"</string>
<string name="clock_seconds" msgid="7689554147579179507">"顯示時鐘秒數"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"在狀態列中顯示時鐘秒數,但可能會影響電池壽命。"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速設定"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"在快速設定顯示亮度"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"在快速設定使用分頁"</string>
+ <string name="experimental" msgid="6198182315536726162">"實驗版"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 702ad53..369172f3 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -437,4 +437,8 @@
<string name="activity_not_found" msgid="348423244327799974">"您的裝置未安裝這個應用程式"</string>
<string name="clock_seconds" msgid="7689554147579179507">"顯示時鐘秒數"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"在狀態列中顯示時鐘秒數。這可能會影響電池續航力。"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速設定"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"在快速設定中顯示亮度"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"在快速設定中使用分頁功能"</string>
+ <string name="experimental" msgid="6198182315536726162">"實驗性"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 8774036..2a14a9f 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -435,4 +435,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Uhlelo lokusebenza alufakiwe kudivayisi yakho"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Bonisa amasekhondi wewashi"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Bonisa amasekhondi wewashi kubha yesimo. Ingathinta impilo yebhethri."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Hlela kabusha izilungiselelo ezisheshayo"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Bonisa ukukhanya kuzilungiselelo ezisheshayo"</string>
+ <string name="qs_paging" msgid="7020133150248666132">"Sebenzisa i-paging kuzilungiselelo ezisheshayo"</string>
+ <string name="experimental" msgid="6198182315536726162">"Okokulinga"</string>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 527248c..c070a0e 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -90,5 +90,9 @@
<declare-styleable name="AlphaOptimizedImageView">
<attr name="hasOverlappingRendering" format="boolean" />
</declare-styleable>
+
+ <declare-styleable name="TunerSwitch">
+ <attr name="defValue" format="boolean" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 0dcbe88..da21084 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -143,4 +143,7 @@
<color name="volume_icon_color">#ffffffff</color>
<color name="volume_settings_icon_color">#7fffffff</color>
<color name="volume_slider_inactive">#FFB0BEC5</color><!-- blue grey 200 -->
+
+ <color name="fab_ripple">#1fffffff</color><!-- 12% white -->
+ <color name="fab_shape">#ff009688</color><!-- Teal 500 -->
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 03ea73c..96a77256 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -164,6 +164,9 @@
<dimen name="pull_span_min">25dp</dimen>
<dimen name="qs_tile_height">88dp</dimen>
+ <dimen name="qs_quick_actions_height">88dp</dimen>
+ <dimen name="qs_quick_actions_padding">25dp</dimen>
+ <dimen name="qs_page_indicator_size">12dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
@@ -587,4 +590,9 @@
<!-- Thickness of the shadows of the assist disclosure beams -->
<dimen name="assist_disclosure_shadow_thickness">1.5dp</dimen>
+
+ <dimen name="fab_size">56dp</dimen>
+ <dimen name="fab_margin">16dp</dimen>
+ <dimen name="fab_elevation">12dp</dimen>
+ <dimen name="fab_press_translation_z">9dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8b3f2d8..db1e688 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1145,4 +1145,20 @@
<!-- Description of setting to show clock seconds [CHAR LIMIT=NONE] -->
<string name="clock_seconds_desc">Show clock seconds in the status bar. May impact battery life.</string>
+ <!-- Button that leads to page to rearrange quick settings tiles [CHAR LIMIT=60] -->
+ <string name="qs_rearrange">Rearrange Quick Settings</string>
+ <!-- Option to show brightness bar in quick settings [CHAR LIMIT=60] -->
+ <string name="show_brightness">Show brightness in Quick Settings</string>
+ <!-- Option to use new paging layout in quick settings [CHAR LIMIT=60] -->
+ <string name="qs_paging">Use paging in Quick Settings</string>
+
+ <!-- Category in the System UI Tuner settings, where new/experimental
+ settings are -->
+ <string name="experimental">Experimental</string>
+
+ <string name="save" translatable="false">Save</string>
+ <string name="qs_customize" translatable="false">Allow long-press customize in Quick Settings</string>
+ <string name="qs_customize_info" translatable="false">Info</string>
+ <string name="qs_customize_remove" translatable="false">Remove</string>
+
</resources>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index beb863c..07e7688d 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -15,11 +15,36 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:title="@string/system_ui_tuner">
- <Preference
- android:key="qs_tuner"
- android:title="@string/quick_settings" />
+ <PreferenceScreen
+ android:title="@string/quick_settings">
+
+ <Preference
+ android:key="qs_tuner"
+ android:title="@string/qs_rearrange" />
+
+ <PreferenceCategory
+ android:title="@string/experimental">
+
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="qs_show_brightness"
+ android:title="@string/show_brightness"
+ sysui:defValue="true" />
+
+ <com.android.systemui.tuner.QSPagingSwitch
+ android:key="qs_paged_panel"
+ android:title="@string/qs_paging" />
+
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="qs_allow_customize"
+ android:title="@string/qs_customize" />
+
+ </PreferenceCategory>
+
+ </PreferenceScreen>
+
<PreferenceScreen
android:title="@string/status_bar" >
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index b0e2afa..1dca149 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -112,7 +112,9 @@
public boolean onScaleBegin(ScaleGestureDetector detector) {
if (DEBUG_SCALE) Log.v(TAG, "onscalebegin()");
- startExpanding(mResizedView, STRETCH);
+ if (!mOnlyMovements) {
+ startExpanding(mResizedView, STRETCH);
+ }
return mExpanding;
}
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/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 6f49fd6..87dfab9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -75,6 +75,8 @@
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)
@@ -675,6 +678,7 @@
playSounds(true);
}
}
+ notifyStartedGoingToSleep();
}
public void onFinishedGoingToSleep(int why) {
@@ -1051,6 +1055,7 @@
// Without this, settings is not enabled until the lock screen first appears
setShowingLocked(false);
hideLocked();
+ mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
return;
}
}
@@ -1095,6 +1100,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);
@@ -1206,6 +1216,9 @@
case VERIFY_UNLOCK:
handleVerifyUnlock();
break;
+ case NOTIFY_STARTED_GOING_TO_SLEEP:
+ handleNotifyStartedGoingToSleep();
+ break;
case NOTIFY_FINISHED_GOING_TO_SLEEP:
handleNotifyFinishedGoingToSleep();
break;
@@ -1543,6 +1556,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
@@ -1640,6 +1660,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/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
new file mode 100644
index 0000000..1200266
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -0,0 +1,86 @@
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+public class PageIndicator extends LinearLayout {
+
+ private final int mPageIndicatorSize;
+
+ public PageIndicator(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setGravity(Gravity.CENTER);
+ mPageIndicatorSize =
+ (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_size);
+ }
+
+ public void setNumPages(int numPages) {
+ while (numPages < getChildCount()) {
+ removeViewAt(getChildCount() - 1);
+ }
+ while (numPages > getChildCount()) {
+ SinglePageIndicator v = new SinglePageIndicator(mContext);
+ v.setAmount(0);
+ addView(v, new LayoutParams(mPageIndicatorSize, mPageIndicatorSize));
+ }
+ }
+
+ public void setLocation(float location) {
+ int index = (int) location;
+ location -= index;
+
+ final int N = getChildCount();
+ for (int i = 0; i < N; i++) {
+ float amount = 0;
+ if (i == index) {
+ amount = 1 - location;
+ } else if (i == index + 1) {
+ amount = location;
+ }
+ ((SinglePageIndicator) getChildAt(i)).setAmount(amount);
+ }
+ }
+
+ // This could be done with a circle drawable and an ImageView, but this seems
+ // easier for now.
+ public static class SinglePageIndicator extends View {
+ private static final int MIN_ALPHA = 0x4d;
+ private static final int MAX_ALPHA = 0xff;
+
+ private static final float MIN_SIZE = .55f;
+ private static final float MAX_SIZE = .7f;
+
+ private final Paint mPaint;
+ private float mSize;
+
+ public SinglePageIndicator(Context context) {
+ super(context);
+ mPaint = new Paint();
+ mPaint.setColor(0xffffffff);
+ mPaint.setAlpha(MAX_ALPHA);
+ }
+
+ public void setAmount(float amount) {
+ mSize = amount * (MAX_SIZE - MIN_SIZE) + MIN_SIZE;
+ int alpha = (int) (amount * (MAX_ALPHA - MIN_ALPHA)) + MIN_ALPHA;
+ mPaint.setAlpha(alpha);
+ postInvalidate();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int minDimen = Math.min(getWidth(), getHeight()) / 2;
+ float radius = mSize * minDimen;
+ float x = getWidth() / 2f;
+ float y = getHeight() / 2f;
+ canvas.drawCircle(x, y, radius, mPaint);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
new file mode 100644
index 0000000..c612600
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -0,0 +1,225 @@
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.internal.widget.PagerAdapter;
+import com.android.internal.widget.ViewPager;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel.QSTileLayout;
+import com.android.systemui.qs.QSPanel.TileRecord;
+
+import java.util.ArrayList;
+
+public class PagedTileLayout extends ViewPager implements QSTileLayout {
+
+ private static final boolean DEBUG = false;
+
+ private static final String TAG = "PagedTileLayout";
+
+ private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
+ private final ArrayList<TilePage> mPages = new ArrayList<TilePage>();
+
+ private FirstPage mFirstPage;
+ private PageIndicator mPageIndicator;
+
+ private int mNumPages;
+
+ public PagedTileLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setAdapter(mAdapter);
+ setOnPageChangeListener(new OnPageChangeListener() {
+ @Override
+ public void onPageSelected(int position) {
+ if (mPageIndicator == null) return;
+ mPageIndicator.setLocation(position);
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ if (mPageIndicator == null) return;
+ mPageIndicator.setLocation(position + positionOffset);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ }
+ });
+ setCurrentItem(0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPageIndicator = (PageIndicator) findViewById(R.id.page_indicator);
+ ((LayoutParams) mPageIndicator.getLayoutParams()).isDecor = true;
+
+ mFirstPage = (FirstPage) findViewById(R.id.first_page);
+ removeView(mFirstPage); // We don't actually want this on the view yet, just inflated.
+ mPages.add(mFirstPage.mTilePage);
+ }
+
+ @Override
+ public int getOffsetTop(TileRecord tile) {
+ if (tile.tileView.getParent() == mFirstPage.mTilePage) {
+ return mFirstPage.getTop() + mFirstPage.mTilePage.getTop();
+ }
+ return ((ViewGroup) tile.tileView.getParent()).getTop();
+ }
+
+ @Override
+ public void setTileVisibility(TileRecord tile, int visibility) {
+ tile.tileView.setVisibility(visibility);
+// // TODO: Do something smarter here.
+// distributeTiles();
+ }
+
+ @Override
+ public void addTile(TileRecord tile) {
+ mTiles.add(tile);
+ distributeTiles();
+ }
+
+ @Override
+ public void removeTile(TileRecord tile) {
+ if (mTiles.remove(tile)) {
+ distributeTiles();
+ }
+ }
+
+ private void distributeTiles() {
+ if (DEBUG) Log.d(TAG, "Distributing tiles");
+ mFirstPage.mQuickQuickTiles.removeAllViews();
+ final int NP = mPages.size();
+ for (int i = 0; i < NP; i++) {
+ mPages.get(i).clear();
+ }
+ int index = 0;
+ final int NT = mTiles.size();
+ for (int i = 0; i < NT; i++) {
+ TileRecord tile = mTiles.get(i);
+ if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
+ tile.tileView.setType(QSTileView.QS_TYPE_QUICK);
+ mFirstPage.mQuickQuickTiles.addView(tile.tileView);
+ continue;
+ }
+ if (mPages.get(index).isFull()) {
+ if (++index == mPages.size()) {
+ if (DEBUG) Log.d(TAG, "Adding page for " + tile.tile.getClass().getSimpleName());
+ mPages.add((TilePage) LayoutInflater.from(mContext)
+ .inflate(R.layout.qs_paged_page, this, false));
+ }
+ }
+ if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
+ + index);
+ mPages.get(index).addTile(tile);
+ }
+ if (mNumPages != index + 1) {
+ mNumPages = index + 1;
+ mPageIndicator.setNumPages(mNumPages);
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void updateResources() {
+ for (int i = 0; i < mPages.size(); i++) {
+ mPages.get(i).updateResources();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ // The ViewPager likes to eat all of the space, instead force it to wrap to the max height
+ // of the pages.
+ int maxHeight = 0;
+ final int N = getChildCount();
+ for (int i = 0; i < N; i++) {
+ int height = getChildAt(i).getMeasuredHeight();
+ if (height > maxHeight) {
+ maxHeight = height;
+ }
+ }
+ setMeasuredDimension(getMeasuredWidth(), maxHeight + mPageIndicator.getMeasuredHeight());
+ }
+
+ public static class FirstPage extends LinearLayout {
+ private LinearLayout mQuickQuickTiles;
+ private TilePage mTilePage;
+
+ public FirstPage(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mQuickQuickTiles = (LinearLayout) findViewById(R.id.quick_tile_layout);
+ mTilePage = (TilePage) findViewById(R.id.tile_page);
+ // Less rows on first page, because it needs room for the quick tiles.
+ mTilePage.mMaxRows = 3;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // The ViewPager will try to make us taller, don't do it unless we need to.
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
+ MeasureSpec.AT_MOST);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ public static class TilePage extends TileLayout {
+ private int mMaxRows = 4;
+
+ public TilePage(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mAllowDual = false;
+ }
+
+ public void setMaxRows(int maxRows) {
+ mMaxRows = maxRows;
+ }
+
+ private void clear() {
+ if (DEBUG) Log.d(TAG, "Clearing page");
+ removeAllViews();
+ mRecords.clear();
+ }
+
+ public boolean isFull() {
+ return mRecords.size() >= mColumns * mMaxRows;
+ }
+ }
+
+ private final PagerAdapter mAdapter = new PagerAdapter() {
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ if (DEBUG) Log.d(TAG, "Destantiating " + position);
+ // TODO: Find way to clean up the extra pages.
+ container.removeView((View) object);
+ }
+
+ public Object instantiateItem(ViewGroup container, int position) {
+ if (DEBUG) Log.d(TAG, "Instantiating " + position);
+ ViewGroup view = position == 0 ? mFirstPage : mPages.get(position);
+ container.addView(view);
+ return view;
+ }
+
+ @Override
+ public int getCount() {
+ return mNumPages;
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b640cf1..880349e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -39,18 +39,25 @@
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile.DetailAdapter;
+import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSlider;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
/** View that represents the quick settings tile panel. **/
-public class QSPanel extends FrameLayout {
+public class QSPanel extends FrameLayout implements Tunable {
- private final Context mContext;
+ public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
+ public static final String QS_PAGED_PANEL = "qs_paged_panel";
+ public static final String QS_ALLOW_CUSTOMIZE = "qs_allow_customize";
+
+ protected final Context mContext;
protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
private final View mDetail;
private final ViewGroup mDetailContent;
@@ -74,8 +81,10 @@
private QSFooter mFooter;
private boolean mGridContentVisible = true;
- private LinearLayout mQsContainer;
- private TileLayout mTileLayout;
+ protected LinearLayout mQsContainer;
+ protected QSTileLayout mTileLayout;
+
+ private QSCustomizer mCustomizePanel;
public QSPanel(Context context) {
this(context, null);
@@ -104,10 +113,7 @@
addView(mQsContainer);
- mTileLayout = new TileLayout(mContext, mRecords);
-
mQsContainer.addView(mBrightnessView);
- mQsContainer.addView(mTileLayout);
mQsContainer.addView(mFooter.getView());
mClipper = new QSDetailClipper(mDetail);
updateResources();
@@ -126,6 +132,53 @@
});
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ TunerService.get(mContext).addTunable(this,
+ QS_SHOW_BRIGHTNESS, QS_PAGED_PANEL, QS_ALLOW_CUSTOMIZE);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ TunerService.get(mContext).removeTunable(this);
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (QS_SHOW_BRIGHTNESS.equals(key)) {
+ mBrightnessView.setVisibility(newValue == null || Integer.parseInt(newValue) != 0
+ ? VISIBLE : GONE);
+ } else if (QS_PAGED_PANEL.equals(key)) {
+ if (mTileLayout != null) {
+ for (int i = 0; i < mRecords.size(); i++) {
+ mTileLayout.removeTile(mRecords.get(i));
+ }
+ mQsContainer.removeView((View) mTileLayout);
+ }
+ int layout = newValue != null && Integer.parseInt(newValue) != 0
+ ? R.layout.qs_paged_tile_layout : R.layout.qs_tile_layout;
+ mTileLayout =
+ (QSTileLayout) LayoutInflater.from(mContext).inflate(layout, mQsContainer, false);
+ mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
+ for (int i = 0; i < mRecords.size(); i++) {
+ mTileLayout.addTile(mRecords.get(i));
+ }
+ } else if (QS_ALLOW_CUSTOMIZE.equals(key)) {
+ if (newValue != null && Integer.parseInt(newValue) != 0) {
+ mCustomizePanel = (QSCustomizer) LayoutInflater.from(mContext)
+ .inflate(R.layout.qs_customize_panel, null);
+ mCustomizePanel.setHost(mHost);
+ } else {
+ if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+ mCustomizePanel.hide();
+ }
+ mCustomizePanel = null;
+ }
+ }
+ }
+
private void updateDetailText() {
mDetailDoneButton.setText(R.string.quick_settings_done);
mDetailSettingsButton.setText(R.string.quick_settings_more_settings);
@@ -164,7 +217,9 @@
refreshAllTiles();
}
updateDetailText();
- mTileLayout.updateResources();
+ if (mTileLayout != null) {
+ mTileLayout.updateResources();
+ }
}
@Override
@@ -185,6 +240,12 @@
mFooter.onConfigurationChanged();
}
+ public void onCollapse() {
+ if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+ mCustomizePanel.hide();
+ }
+ }
+
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
@@ -240,18 +301,18 @@
mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
}
- private void setTileVisibility(View v, int visibility) {
- mHandler.obtainMessage(H.SET_TILE_VISIBILITY, visibility, 0, v).sendToTarget();
+ private void setTileVisibility(TileRecord record, int visibility) {
+ mHandler.obtainMessage(H.SET_TILE_VISIBILITY, visibility, 0, record).sendToTarget();
}
- private void handleSetTileVisibility(View v, int visibility) {
- if (visibility == v.getVisibility()) return;
- v.setVisibility(visibility);
+ private void handleSetTileVisibility(TileRecord tile, int visibility) {
+ if (visibility == tile.tileView.getVisibility()) return;
+ mTileLayout.setTileVisibility(tile, visibility);
}
public void setTiles(Collection<QSTile<?>> tiles) {
for (TileRecord record : mRecords) {
- removeView(record.tileView);
+ mTileLayout.removeTile(record);
}
mRecords.clear();
for (QSTile<?> tile : tiles) {
@@ -264,11 +325,11 @@
private void drawTile(TileRecord r, QSTile.State state) {
final int visibility = state.visible ? VISIBLE : GONE;
- setTileVisibility(r.tileView, visibility);
+ setTileVisibility(r, visibility);
r.tileView.onStateChanged(state);
}
- private void addTile(final QSTile<?> tile) {
+ protected void addTile(final QSTile<?> tile) {
final TileRecord r = new TileRecord();
r.tile = tile;
r.tileView = tile.createTileView(mContext);
@@ -319,7 +380,13 @@
final View.OnLongClickListener longClick = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- r.tile.longClick();
+ if (mCustomizePanel != null) {
+ if (!mCustomizePanel.isCustomizing()) {
+ mCustomizePanel.show();
+ }
+ } else {
+ r.tile.longClick();
+ }
return true;
}
};
@@ -329,14 +396,22 @@
r.tile.refreshState();
mRecords.add(r);
- mTileLayout.addView(r.tileView);
+ if (mTileLayout != null) {
+ mTileLayout.addTile(r);
+ }
}
public boolean isShowingDetail() {
- return mDetailRecord != null;
+ return mDetailRecord != null
+ || (mCustomizePanel != null && mCustomizePanel.isCustomizing());
}
public void closeDetail() {
+ if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+ // Treat this as a detail panel for now, to make things easy.
+ mCustomizePanel.hide();
+ return;
+ }
showDetail(false, mDetailRecord);
}
@@ -371,7 +446,7 @@
}
r.tile.setDetailListening(show);
int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
- int y = r.tileView.getTop() + mTileLayout.getTop() + r.tileView.getHeight() / 2;
+ int y = r.tileView.getTop() + mTileLayout.getOffsetTop(r) + r.tileView.getHeight() / 2;
handleShowDetailImpl(r, show, x, y);
}
@@ -474,7 +549,7 @@
if (msg.what == SHOW_DETAIL) {
handleShowDetail((Record)msg.obj, msg.arg1 != 0);
} else if (msg.what == SET_TILE_VISIBILITY) {
- handleSetTileVisibility((View)msg.obj, msg.arg1);
+ handleSetTileVisibility((TileRecord) msg.obj, msg.arg1);
}
}
}
@@ -486,7 +561,7 @@
int y;
}
- protected static final class TileRecord extends Record {
+ public static final class TileRecord extends Record {
public QSTile<?> tile;
public QSTileView tileView;
public int row;
@@ -534,4 +609,12 @@
void onToggleStateChanged(boolean state);
void onScanStateChanged(boolean state);
}
+
+ public interface QSTileLayout {
+ void addTile(TileRecord tile);
+ void removeTile(TileRecord tile);
+ void setTileVisibility(TileRecord tile, int visibility);
+ int getOffsetTop(TileRecord tile);
+ void updateResources();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index b330582..3b3593b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -65,6 +65,8 @@
private TState mTmpState = newTileState();
private boolean mAnnounceNextStateChange;
+ private String mTileSpec;
+
abstract protected TState newTileState();
abstract protected void handleClick();
abstract protected void handleUpdateState(TState state, Object arg);
@@ -84,8 +86,20 @@
mHandler = new H(host.getLooper());
}
- public boolean supportsDualTargets() {
- return false;
+ public String getTileSpec() {
+ return mTileSpec;
+ }
+
+ public void setTileSpec(String tileSpec) {
+ mTileSpec = tileSpec;
+ }
+
+ public int getTileType() {
+ return QSTileView.QS_TYPE_NORMAL;
+ }
+
+ public final boolean supportsDualTargets() {
+ return getTileType() == QSTileView.QS_TYPE_DUAL;
}
public Host getHost() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 6d26a3b..08cdc1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -48,6 +48,10 @@
private static final Typeface CONDENSED = Typeface.create("sans-serif-condensed",
Typeface.NORMAL);
+ public static final int QS_TYPE_NORMAL = 0;
+ public static final int QS_TYPE_DUAL = 1;
+ public static final int QS_TYPE_QUICK = 2;
+
protected final Context mContext;
private final View mIcon;
private final View mDivider;
@@ -61,13 +65,15 @@
private TextView mLabel;
private QSDualTileLabel mDualLabel;
- private boolean mDual;
+ private int mType;
private OnClickListener mClickPrimary;
private OnClickListener mClickSecondary;
private OnLongClickListener mLongClick;
private Drawable mTileBackground;
private RippleDrawable mRipple;
+ private View mCircle;
+
public QSTileView(Context context) {
super(context);
@@ -89,6 +95,9 @@
mIcon = createIcon();
addView(mIcon);
+ mCircle = createCircleIcon();
+ addView(mCircle);
+
mDivider = new View(mContext);
mDivider.setBackgroundColor(context.getColor(R.color.qs_tile_divider));
final int dh = res.getDimensionPixelSize(R.dimen.qs_tile_divider_height);
@@ -131,12 +140,12 @@
}
if (mDualLabel != null) {
labelText = mDualLabel.getText();
- labelDescription = mLabel.getContentDescription();
+ labelDescription = mLabel != null ? mLabel.getContentDescription() : null;
removeView(mDualLabel);
mDualLabel = null;
}
final Resources res = mContext.getResources();
- if (mDual) {
+ if (mType == QS_TYPE_DUAL) {
mDualLabel = new QSDualTileLabel(mContext);
mDualLabel.setId(View.generateViewId());
mDualLabel.setBackgroundResource(R.drawable.btn_borderless_rect);
@@ -157,7 +166,7 @@
}
addView(mDualLabel);
mDualLabel.setAccessibilityTraversalAfter(mTopBackgroundView.getId());
- } else {
+ } else if (mType == QS_TYPE_NORMAL) {
mLabel = new TextView(mContext);
mLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
mLabel.setGravity(Gravity.CENTER_HORIZONTAL);
@@ -174,16 +183,16 @@
}
}
- public boolean setDual(boolean dual) {
- final boolean changed = dual != mDual;
- mDual = dual;
+ public boolean setType(int type) {
+ final boolean changed = mType != type;
+ mType = type;
if (changed) {
recreateLabel();
}
if (mTileBackground instanceof RippleDrawable) {
setRipple((RippleDrawable) mTileBackground);
}
- if (dual) {
+ if (mType == QS_TYPE_DUAL) {
mTopBackgroundView.setOnClickListener(mClickPrimary);
setOnClickListener(null);
setClickable(false);
@@ -197,9 +206,10 @@
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
setBackground(mTileBackground);
}
- mTopBackgroundView.setFocusable(dual);
- setFocusable(!dual);
- mDivider.setVisibility(dual ? VISIBLE : GONE);
+ mTopBackgroundView.setFocusable(mType == QS_TYPE_DUAL);
+ setFocusable(mType != QS_TYPE_DUAL);
+ mDivider.setVisibility(mType == QS_TYPE_DUAL ? VISIBLE : GONE);
+ mCircle.setVisibility(mType == QS_TYPE_QUICK ? VISIBLE : GONE);
postInvalidate();
return changed;
}
@@ -225,6 +235,21 @@
return icon;
}
+ protected View createCircleIcon() {
+ final ImageView icon = new ImageView(mContext);
+ icon.setImageResource(R.drawable.ic_qs_circle);
+ // TODO: Not this.
+ icon.setPadding(20, 20, 20, 20);
+ return icon;
+ }
+
+ protected View createCircle() {
+ final ImageView icon = new ImageView(mContext);
+ icon.setId(android.R.id.icon);
+ icon.setScaleType(ScaleType.CENTER_INSIDE);
+ return icon;
+ }
+
private Drawable newTileBackground() {
final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
final TypedArray ta = mContext.obtainStyledAttributes(attrs);
@@ -234,7 +259,7 @@
}
private View labelView() {
- return mDual ? mDualLabel : mLabel;
+ return mType == QS_TYPE_DUAL ? mDualLabel : mLabel;
}
@Override
@@ -243,9 +268,18 @@
final int h = MeasureSpec.getSize(heightMeasureSpec);
final int iconSpec = exactly(mIconSizePx);
mIcon.measure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.AT_MOST), iconSpec);
- labelView().measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST));
- if (mDual) {
- mDivider.measure(widthMeasureSpec, exactly(mDivider.getLayoutParams().height));
+ switch (mType) {
+ case QS_TYPE_QUICK:
+ mCircle.measure(
+ MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY));
+ break;
+ case QS_TYPE_DUAL:
+ mDivider.measure(widthMeasureSpec, exactly(mDivider.getLayoutParams().height));
+ default:
+ labelView().measure(widthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST));
+ break;
}
int heightSpec = exactly(
mIconSizePx + mTilePaddingBelowIconPx + mTilePaddingTopPx);
@@ -268,6 +302,10 @@
top += mTileSpacingPx;
top += mTilePaddingTopPx;
final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
+ if (mType == QS_TYPE_QUICK) {
+ top = (h - mIcon.getMeasuredHeight()) / 2;
+ layout(mCircle, 0, 0);
+ }
layout(mIcon, iconLeft, top);
if (mRipple != null) {
updateRippleSize(w, h);
@@ -275,17 +313,19 @@
}
top = mIcon.getBottom();
top += mTilePaddingBelowIconPx;
- if (mDual) {
+ if (mType == QS_TYPE_DUAL) {
layout(mDivider, 0, top);
top = mDivider.getBottom();
}
- layout(labelView(), 0, top);
+ if (mType != QS_TYPE_QUICK) {
+ layout(labelView(), 0, top);
+ }
}
private void updateRippleSize(int width, int height) {
// center the touch feedback on the center of the icon, and dial it down a bit
final int cx = width / 2;
- final int cy = mDual ? mIcon.getTop() + mIcon.getHeight() / 2 : height / 2;
+ final int cy = mType == QS_TYPE_DUAL ? mIcon.getTop() + mIcon.getHeight() / 2 : height / 2;
final int rad = (int)(mIcon.getHeight() * 1.25f);
mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
}
@@ -298,11 +338,11 @@
if (mIcon instanceof ImageView) {
setIcon((ImageView) mIcon, state);
}
- if (mDual) {
+ if (mType == QS_TYPE_DUAL) {
mDualLabel.setText(state.label);
mDualLabel.setContentDescription(state.dualLabelContentDescription);
mTopBackgroundView.setContentDescription(state.contentDescription);
- } else {
+ } else if (mType == QS_TYPE_NORMAL) {
mLabel.setText(state.label);
setContentDescription(state.contentDescription);
}
@@ -338,7 +378,7 @@
public View updateAccessibilityOrder(View previousView) {
View firstView;
View lastView;
- if (mDual) {
+ if (mType == QS_TYPE_DUAL) {
lastView = mDualLabel;
firstView = mTopBackgroundView;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
new file mode 100644
index 0000000..bb2340c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
@@ -0,0 +1,28 @@
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+public class QuickTileLayout extends LinearLayout {
+
+ public QuickTileLayout(Context context) {
+ this(context, null);
+ }
+
+ public QuickTileLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setGravity(Gravity.CENTER);
+ }
+
+ @Override
+ public void addView(View child, int index, ViewGroup.LayoutParams params) {
+ // Make everything square at the height of this view.
+ params = new LayoutParams(params.height, params.height);
+ ((LinearLayout.LayoutParams) params).weight = 1;
+ super.addView(child, index, params);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 02b8fe3..8bd05fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -2,35 +2,68 @@
import android.content.Context;
import android.content.res.Resources;
+import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanel.TileRecord;
import java.util.ArrayList;
-public class TileLayout extends ViewGroup {
+public class TileLayout extends ViewGroup implements QSTileLayout {
private static final float TILE_ASPECT = 1.2f;
private static final String TAG = "TileLayout";
private int mDualTileUnderlap;
- private int mColumns;
+ protected int mColumns;
private int mCellWidth;
private int mCellHeight;
private int mLargeCellWidth;
private int mLargeCellHeight;
- private final ArrayList<TileRecord> mRecords;
+ protected boolean mAllowDual = true;
- public TileLayout(Context context, ArrayList<TileRecord> records) {
- super(context);
- mRecords = records;
+ protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
+
+ public TileLayout(Context context) {
+ this(context, null);
+ }
+
+ public TileLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
updateResources();
}
+ @Override
+ public int getOffsetTop(TileRecord tile) {
+ return getTop();
+ }
+
+ public void addTile(TileRecord tile) {
+ mRecords.add(tile);
+ addView(tile.tileView);
+ }
+
+ @Override
+ public void removeTile(TileRecord tile) {
+ mRecords.remove(tile);
+ removeView(tile.tileView);
+ }
+
+ public void removeAllViews() {
+ mRecords.clear();
+ super.removeAllViews();
+ }
+
+ @Override
+ public void setTileVisibility(TileRecord tile, int visibility) {
+ tile.tileView.setVisibility(visibility);
+ }
+
public void updateResources() {
final Resources res = mContext.getResources();
final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
@@ -56,10 +89,11 @@
if (record.tileView.getVisibility() == GONE) continue;
// wrap to next column if we've reached the max # of columns
// also don't allow dual + single tiles on the same row
- if (r == -1 || c == (mColumns - 1) || rowIsDual != record.tile.supportsDualTargets()) {
+ if (r == -1 || c == (mColumns - 1)
+ || rowIsDual != (mAllowDual && record.tile.supportsDualTargets())) {
r++;
c = 0;
- rowIsDual = record.tile.supportsDualTargets();
+ rowIsDual = mAllowDual && record.tile.supportsDualTargets();
} else {
c++;
}
@@ -70,7 +104,8 @@
View previousView = this;
for (TileRecord record : mRecords) {
- if (record.tileView.setDual(record.tile.supportsDualTargets())) {
+ if (record.tileView.setType(mAllowDual ? record.tile.getTileType()
+ : QSTileView.QS_TYPE_NORMAL)) {
record.tileView.handleStateChanged(record.tile.getState());
}
if (record.tileView.getVisibility() == GONE) continue;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
new file mode 100644
index 0000000..8866e55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.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 com.android.systemui.qs.customize;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.phone.QSTileHost;
+
+/**
+ * A version of QSPanel that allows tiles to be dragged around rather than
+ * clicked on. Dragging starting and receiving is handled in the NonPagedTileLayout,
+ * and the saving/ordering is handled by the CustomQSTileHost.
+ */
+public class CustomQSPanel extends QSPanel {
+
+ private CustomQSTileHost mCustomHost;
+
+ public CustomQSPanel(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mTileLayout = (QSTileLayout) LayoutInflater.from(mContext)
+ .inflate(R.layout.qs_customize_layout, mQsContainer, false);
+ mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
+ ((NonPagedTileLayout) mTileLayout).setCustomQsPanel(this);
+ }
+
+ @Override
+ public void setHost(QSTileHost host) {
+ super.setHost(host);
+ mCustomHost = (CustomQSTileHost) host;
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (key.equals(QS_SHOW_BRIGHTNESS)) {
+ // No Brightness for you.
+ super.onTuningChanged(key, "0");
+ }
+ }
+
+ public CustomQSTileHost getCustomHost() {
+ return mCustomHost;
+ }
+
+ public void tileSelected(QSTile<?> tile, ClipData currentClip) {
+ String sourceSpec = getSpec(currentClip);
+ String destSpec = tile.getTileSpec();
+ if (!sourceSpec.equals(destSpec)) {
+ mCustomHost.moveTo(sourceSpec, destSpec);
+ }
+ }
+
+ public ClipData getClip(QSTile<?> tile) {
+ String tileSpec = tile.getTileSpec();
+ // TODO: Something better than plain text.
+ // TODO: Once using something better than plain text, stop listening to non-QS drag events.
+ return ClipData.newPlainText(tileSpec, tileSpec);
+ }
+
+ public String getSpec(ClipData data) {
+ return data.getItemAt(0).getText().toString();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
new file mode 100644
index 0000000..84b05d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
@@ -0,0 +1,183 @@
+/*
+ * 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.qs.customize;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.policy.SecurityController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @see CustomQSPanel
+ */
+public class CustomQSTileHost extends QSTileHost {
+
+ private static final String TAG = "CustomHost";
+ private List<String> mTiles;
+ private List<String> mSavedTiles;
+ private ArrayList<String> mStash;
+
+ public CustomQSTileHost(Context context, QSTileHost host) {
+ super(context, null, host.getBluetoothController(), host.getLocationController(),
+ host.getRotationLockController(), host.getNetworkController(),
+ host.getZenModeController(), host.getHotspotController(), host.getCastController(),
+ host.getFlashlightController(), host.getUserSwitcherController(),
+ host.getKeyguardMonitor(), new BlankSecurityController());
+ }
+
+ @Override
+ protected QSTile<?> createTile(String tileSpec) {
+ QSTile<?> tile = super.createTile(tileSpec);
+ tile.setTileSpec(tileSpec);
+ return tile;
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ // No Tunings For You.
+ if (TILES_SETTING.equals(key)) {
+ mSavedTiles = super.loadTileSpecs(newValue);
+ }
+ }
+
+ public void setSavedTiles() {
+ setTiles(mSavedTiles);
+ }
+
+ public void saveCurrentTiles() {
+ Secure.putStringForUser(getContext().getContentResolver(), TILES_SETTING,
+ TextUtils.join(",", mTiles), ActivityManager.getCurrentUser());
+ }
+
+ public void stashCurrentTiles() {
+ mStash = new ArrayList<>(mTiles);
+ }
+
+ public void unstashTiles() {
+ setTiles(mStash);
+ }
+
+ public void moveTo(String from, String to) {
+ int fromIndex = mTiles.indexOf(from);
+ if (fromIndex < 0) {
+ Log.e(TAG, "Unknown from tile " + from);
+ return;
+ }
+ int index = mTiles.indexOf(to);
+ if (index < 0) {
+ Log.e(TAG, "Unknown to tile " + to);
+ return;
+ }
+ mTiles.remove(fromIndex);
+ mTiles.add(index, from);
+ super.onTuningChanged(TILES_SETTING, null);
+ }
+
+ public void remove(String spec) {
+ if (!mTiles.remove(spec)) {
+ Log.e(TAG, "Unknown remove spec " + spec);
+ }
+ super.onTuningChanged(TILES_SETTING, null);
+ }
+
+ public void setTiles(List<String> tiles) {
+ mTiles = new ArrayList<>(tiles);
+ super.onTuningChanged(TILES_SETTING, null);
+ }
+
+ @Override
+ protected List<String> loadTileSpecs(String tileList) {
+ return mTiles;
+ }
+
+ public void replace(String oldTile, String newTile) {
+ if (oldTile.equals(newTile)) {
+ return;
+ }
+ MetricsLogger.action(getContext(), MetricsLogger.TUNER_QS_REORDER, oldTile + ","
+ + newTile);
+ List<String> order = new ArrayList<>(mTileSpecs);
+ int index = order.indexOf(oldTile);
+ if (index < 0) {
+ Log.e(TAG, "Can't find " + oldTile);
+ return;
+ }
+ order.remove(newTile);
+ order.add(index, newTile);
+ setTiles(order);
+ }
+
+ /**
+ * Blank so that the customizing QS view doesn't show any security messages in the footer.
+ */
+ private static class BlankSecurityController implements SecurityController {
+ @Override
+ public boolean hasDeviceOwner() {
+ return false;
+ }
+
+ @Override
+ public boolean hasProfileOwner() {
+ return false;
+ }
+
+ @Override
+ public String getDeviceOwnerName() {
+ return null;
+ }
+
+ @Override
+ public String getProfileOwnerName() {
+ return null;
+ }
+
+ @Override
+ public boolean isVpnEnabled() {
+ return false;
+ }
+
+ @Override
+ public String getPrimaryVpnName() {
+ return null;
+ }
+
+ @Override
+ public String getProfileVpnName() {
+ return null;
+ }
+
+ @Override
+ public void onUserSwitched(int newUserId) {
+ }
+
+ @Override
+ public void addCallback(SecurityControllerCallback callback) {
+ }
+
+ @Override
+ public void removeCallback(SecurityControllerCallback callback) {
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java b/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java
new file mode 100644
index 0000000..0e15f2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java
@@ -0,0 +1,70 @@
+/*
+ * 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.qs.customize;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.view.View;
+import android.view.View.OnDragListener;
+import android.widget.TextView;
+
+public class DropButton extends TextView implements OnDragListener {
+
+ private OnDropListener mListener;
+
+ public DropButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ // TODO: Don't do this, instead make this view the right size...
+ ((View) getParent()).setOnDragListener(this);
+ }
+
+ public void setOnDropListener(OnDropListener listener) {
+ mListener = listener;
+ }
+
+ private void setHovering(boolean hovering) {
+ setAlpha(hovering ? .5f : 1);
+ }
+
+ @Override
+ public boolean onDrag(View v, DragEvent event) {
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_ENTERED:
+ setHovering(true);
+ break;
+ case DragEvent.ACTION_DROP:
+ if (mListener != null) {
+ mListener.onDrop(this, event.getClipData());
+ }
+ case DragEvent.ACTION_DRAG_EXITED:
+ case DragEvent.ACTION_DRAG_ENDED:
+ setHovering(false);
+ break;
+ }
+ return true;
+ }
+
+ public interface OnDropListener {
+ void onDrop(View v, ClipData data);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/FloatingActionButton.java b/packages/SystemUI/src/com/android/systemui/qs/customize/FloatingActionButton.java
new file mode 100644
index 0000000..8791a10
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/FloatingActionButton.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 com.android.systemui.qs.customize;
+
+import android.animation.AnimatorInflater;
+import android.content.Context;
+import android.graphics.Outline;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class FloatingActionButton extends ImageView {
+
+ public FloatingActionButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setScaleType(ScaleType.CENTER);
+ setStateListAnimator(AnimatorInflater.loadStateListAnimator(context, R.anim.fab_elevation));
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setOval(0, 0, getWidth(), getHeight());
+ }
+ });
+ setClipToOutline(true);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ invalidateOutline();
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
new file mode 100644
index 0000000..012633c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.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.qs.customize;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.PagedTileLayout;
+import com.android.systemui.qs.PagedTileLayout.TilePage;
+import com.android.systemui.qs.QSPanel.QSTileLayout;
+import com.android.systemui.qs.QSPanel.TileRecord;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileView;
+import com.android.systemui.qs.QuickTileLayout;
+
+import java.util.ArrayList;
+
+/**
+ * Similar to PagedTileLayout, except that instead of pages it lays them out
+ * vertically and expects to be inside a ScrollView.
+ * @see CustomQSPanel
+ */
+public class NonPagedTileLayout extends LinearLayout implements QSTileLayout, OnTouchListener {
+
+ private QuickTileLayout mQuickTiles;
+ private final ArrayList<TilePage> mPages = new ArrayList<>();
+ private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
+ private CustomQSPanel mPanel;
+ private final Rect mHitRect = new Rect();
+
+ private ClipData mCurrentClip;
+ private View mCurrentView;
+
+ public NonPagedTileLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mQuickTiles = (QuickTileLayout) findViewById(R.id.quick_tile_layout);
+ TilePage page = (PagedTileLayout.TilePage) findViewById(R.id.tile_page);
+ page.setMaxRows(3 /* First page only gets 3 */);
+ mPages.add(page);
+ }
+
+ public void setCustomQsPanel(CustomQSPanel qsPanel) {
+ mPanel = qsPanel;
+ }
+
+ @Override
+ public void addTile(TileRecord record) {
+ mTiles.add(record);
+ distributeTiles();
+ if (record.tile.getTileType() == QSTileView.QS_TYPE_QUICK
+ || record.tileView.getTag() == record.tile) {
+ return;
+ }
+ record.tileView.setTag(record.tile);
+ record.tileView.setVisibility(View.VISIBLE);
+ record.tileView.init(null, null, null);
+ record.tileView.setOnTouchListener(this);
+ if (mCurrentClip != null
+ && mCurrentClip.getItemAt(0).getText().toString().equals(record.tile.getTileSpec())) {
+ record.tileView.setAlpha(.3f);
+ mCurrentView = record.tileView;
+ }
+ }
+
+ @Override
+ public void removeTile(TileRecord tile) {
+ if (mTiles.remove(tile)) {
+ distributeTiles();
+ }
+ }
+
+ private void distributeTiles() {
+ mQuickTiles.removeAllViews();
+ final int NP = mPages.size();
+ for (int i = 0; i < NP; i++) {
+ mPages.get(i).removeAllViews();
+ }
+ int index = 0;
+ final int NT = mTiles.size();
+ for (int i = 0; i < NT; i++) {
+ TileRecord tile = mTiles.get(i);
+ if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
+ tile.tileView.setType(QSTileView.QS_TYPE_QUICK);
+ mQuickTiles.addView(tile.tileView);
+ continue;
+ }
+ mPages.get(index).addTile(tile);
+ if (mPages.get(index).isFull()) {
+ if (++index == mPages.size()) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ inflater.inflate(R.layout.horizontal_divider, this);
+ mPages.add((TilePage) inflater.inflate(R.layout.qs_paged_page, this, false));
+ addView(mPages.get(mPages.size() - 1));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setTileVisibility(TileRecord tile, int visibility) {
+ // All tiles visible here, so that they can be re-arranged.
+ tile.tileView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public int getOffsetTop(TileRecord tile) {
+ // No touch feedback, so this isn't required.
+ return 0;
+ }
+
+ @Override
+ public void updateResources() {
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_LOCATION:
+ float x = event.getX();
+ float y = event.getY();
+ if (contains(mQuickTiles, x, y)) {
+ // TODO: Reset to pre-drag state.
+ } else {
+ final int NP = mPages.size();
+ for (int i = 0; i < NP; i++) {
+ TilePage page = mPages.get(i);
+ if (contains(page, x, y)) {
+ x -= page.getLeft();
+ y -= page.getTop();
+ final int NC = page.getChildCount();
+ for (int j = 0; j < NC; j++) {
+ View child = page.getChildAt(j);
+ if (contains(child, x, y)) {
+ mPanel.tileSelected((QSTile<?>) child.getTag(), mCurrentClip);
+ }
+ }
+ break;
+ }
+ }
+ }
+ break;
+ case DragEvent.ACTION_DRAG_ENDED:
+ onDragEnded();
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ // Stash the current tiles, in case the drop is on info, that we can restore
+ // the previous state.
+ mPanel.getCustomHost().stashCurrentTiles();
+ mCurrentView = v;
+ mCurrentClip = mPanel.getClip((QSTile<?>) v.getTag());
+ View.DragShadowBuilder shadow = new View.DragShadowBuilder(v);
+ ((View) getParent().getParent()).startDrag(mCurrentClip, shadow, null, 0);
+ v.setAlpha(.3f);
+ return true;
+ }
+ return false;
+ }
+
+ public void onDragEnded() {
+ mCurrentView.setAlpha(1f);
+ mCurrentView = null;
+ mCurrentClip = null;
+ }
+
+ private boolean contains(View v, float x, float y) {
+ v.getHitRect(mHitRect);
+ return mHitRect.contains((int) x, (int) y);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
new file mode 100644
index 0000000..7e74785
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -0,0 +1,200 @@
+/*
+ * 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.qs.customize;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.DragEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.Toolbar;
+import android.widget.Toolbar.OnMenuItemClickListener;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.qs.QSTile.Host.Callback;
+import com.android.systemui.qs.customize.DropButton.OnDropListener;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.tuner.QSPagingSwitch;
+
+import java.util.ArrayList;
+
+/**
+ * Allows full-screen customization of QS, through show() and hide().
+ *
+ * This adds itself to the status bar window, so it can appear on top of quick settings and
+ * *someday* do fancy animations to get into/out of it.
+ */
+public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener, Callback,
+ OnDropListener, OnClickListener {
+
+ private static final int MENU_SAVE = Menu.FIRST;
+ private static final int MENU_RESET = Menu.FIRST + 1;
+
+ private PhoneStatusBar mPhoneStatusBar;
+
+ private Toolbar mToolbar;
+ private ViewGroup mDragButtons;
+ private CustomQSPanel mQsPanel;
+
+ private boolean isShown;
+ private CustomQSTileHost mHost;
+ private DropButton mInfoButton;
+ private DropButton mRemoveButton;
+ private FloatingActionButton mFab;
+
+ public QSCustomizer(Context context, AttributeSet attrs) {
+ super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
+ mPhoneStatusBar = ((SystemUIApplication) mContext.getApplicationContext())
+ .getComponent(PhoneStatusBar.class);
+ }
+
+ public void setHost(QSTileHost host) {
+ mHost = new CustomQSTileHost(mContext, host);
+ mHost.setCallback(this);
+ mQsPanel.setTiles(mHost.getTiles());
+ mQsPanel.setHost(mHost);
+ mHost.setSavedTiles();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mToolbar = (Toolbar) findViewById(com.android.internal.R.id.action_bar);
+ TypedValue value = new TypedValue();
+ mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
+ mToolbar.setNavigationIcon(
+ getResources().getDrawable(value.resourceId, mContext.getTheme()));
+ mToolbar.setNavigationOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // TODO: Is this all we want...?
+ hide();
+ }
+ });
+ mToolbar.setOnMenuItemClickListener(this);
+ mToolbar.getMenu().add(Menu.NONE, MENU_SAVE, 0, mContext.getString(R.string.save))
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
+ mContext.getString(com.android.internal.R.string.reset));
+
+ mQsPanel = (CustomQSPanel) findViewById(R.id.quick_settings_panel);
+
+ mDragButtons = (ViewGroup) findViewById(R.id.drag_buttons);
+ setDragging(false);
+
+ mInfoButton = (DropButton) findViewById(R.id.info_button);
+ mInfoButton.setOnDropListener(this);
+ mRemoveButton = (DropButton) findViewById(R.id.remove_button);
+ mRemoveButton.setOnDropListener(this);
+
+ mFab = (FloatingActionButton) findViewById(R.id.fab);
+ mFab.setImageResource(R.drawable.ic_add);
+ mFab.setOnClickListener(this);
+ }
+
+ public void show() {
+ isShown = true;
+ mHost.setSavedTiles();
+ // TODO: Fancy shmancy reveal.
+ mPhoneStatusBar.getStatusBarWindow().addView(this);
+ }
+
+ public void hide() {
+ isShown = false;
+ // TODO: Similarly awesome or better hide.
+ mPhoneStatusBar.getStatusBarWindow().removeView(this);
+ }
+
+ public boolean isCustomizing() {
+ return isShown;
+ }
+
+ private void reset() {
+ ArrayList<String> tiles = new ArrayList<>();
+ for (String tile : QSPagingSwitch.QS_PAGE_TILES.split(",")) {
+ tiles.add(tile);
+ }
+ mHost.setTiles(tiles);
+ }
+
+ private void setDragging(boolean dragging) {
+ mToolbar.setVisibility(!dragging ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ private void save() {
+ mHost.saveCurrentTiles();
+ hide();
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case MENU_SAVE:
+ save();
+ break;
+ case MENU_RESET:
+ reset();
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public void onTilesChanged() {
+ mQsPanel.setTiles(mHost.getTiles());
+ }
+
+ public boolean onDragEvent(DragEvent event) {
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ setDragging(true);
+ break;
+ case DragEvent.ACTION_DRAG_ENDED:
+ setDragging(false);
+ break;
+ }
+ return true;
+ }
+
+ public void onDrop(View v, ClipData data) {
+ if (v == mRemoveButton) {
+ mHost.remove(mQsPanel.getSpec(data));
+ } else if (v == mInfoButton) {
+ mHost.unstashTiles();
+ SystemUIDialog dialog = new SystemUIDialog(mContext);
+ dialog.setTitle(mQsPanel.getSpec(data));
+ dialog.setPositiveButton(R.string.ok, null);
+ dialog.show();
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mFab == v) {
+ // TODO: Show list of tiles.
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index abce31f..fd70d02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -31,6 +31,7 @@
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileView;
import com.android.systemui.statusbar.policy.BluetoothController;
import java.util.Collection;
@@ -43,15 +44,18 @@
private final BluetoothController mController;
private final BluetoothDetailAdapter mDetailAdapter;
- public BluetoothTile(Host host) {
+ private final boolean mAlwaysDetail;
+
+ public BluetoothTile(Host host, boolean alwaysDetail) {
super(host);
+ mAlwaysDetail = alwaysDetail;
mController = host.getBluetoothController();
mDetailAdapter = new BluetoothDetailAdapter();
}
@Override
- public boolean supportsDualTargets() {
- return true;
+ public int getTileType() {
+ return QSTileView.QS_TYPE_DUAL;
}
@Override
@@ -75,6 +79,10 @@
@Override
protected void handleClick() {
+ if (mAlwaysDetail) {
+ handleSecondaryClick();
+ return;
+ }
final boolean isEnabled = (Boolean)mState.value;
MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled);
mController.setBluetoothEnabled(!isEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java
new file mode 100644
index 0000000..13ccaaa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.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.qs.tiles;
+
+import com.android.systemui.qs.QSTileView;
+
+/** Quick settings tile: Wifi **/
+public class QAirplaneTile extends AirplaneModeTile {
+
+ public QAirplaneTile(Host host) {
+ super(host);
+ }
+
+ @Override
+ public int getTileType() {
+ return QSTileView.QS_TYPE_QUICK;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java
new file mode 100644
index 0000000..02975cb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.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.qs.tiles;
+
+import com.android.systemui.qs.QSTileView;
+
+/** Quick settings tile: Bluetooth **/
+public class QBluetoothTile extends BluetoothTile {
+
+ public QBluetoothTile(Host host) {
+ super(host, false);
+ }
+
+ @Override
+ public int getTileType() {
+ return QSTileView.QS_TYPE_QUICK;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java
new file mode 100644
index 0000000..31035cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.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.qs.tiles;
+
+import com.android.systemui.qs.QSTileView;
+
+/** Quick settings tile: Wifi **/
+public class QFlashlightTile extends FlashlightTile {
+
+ public QFlashlightTile(Host host) {
+ super(host);
+ }
+
+ @Override
+ public int getTileType() {
+ return QSTileView.QS_TYPE_QUICK;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java
new file mode 100644
index 0000000..e066bab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.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.qs.tiles;
+
+import com.android.systemui.qs.QSTileView;
+
+/** Quick settings tile: Rotation **/
+public class QRotationLockTile extends RotationLockTile {
+
+ public QRotationLockTile(Host host) {
+ super(host);
+ }
+
+ @Override
+ public int getTileType() {
+ return QSTileView.QS_TYPE_QUICK;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
new file mode 100644
index 0000000..87194fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.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.qs.tiles;
+
+import com.android.systemui.qs.QSTileView;
+
+/** Quick settings tile: Wifi **/
+public class QWifiTile extends WifiTile {
+
+ public QWifiTile(Host host) {
+ super(host, false);
+ }
+
+ @Override
+ public int getTileType() {
+ return QSTileView.QS_TYPE_QUICK;
+ }
+}
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 e654efd..3295e14 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -50,16 +50,19 @@
private final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
- public WifiTile(Host host) {
+ private final boolean mAlwaysDetail;
+
+ public WifiTile(Host host, boolean alwaysDetail) {
super(host);
+ mAlwaysDetail = alwaysDetail;
mController = host.getNetworkController();
mWifiController = mController.getAccessPointController();
mDetailAdapter = new WifiDetailAdapter();
}
@Override
- public boolean supportsDualTargets() {
- return true;
+ public int getTileType() {
+ return QSTileView.QS_TYPE_DUAL;
}
@Override
@@ -97,6 +100,10 @@
@Override
protected void handleClick() {
+ if (mAlwaysDetail) {
+ handleSecondaryClick();
+ return;
+ }
mState.copyTo(mStateBeforeClick);
MetricsLogger.action(mContext, getMetricsCategory(), !mState.enabled);
mController.setWifiEnabled(!mState.enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index b701e0b..300ea2a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -197,6 +197,7 @@
break;
case PLACE_FULL:
// Nothing to change.
+ mBounds[0] = null;
break;
}
@@ -213,10 +214,13 @@
dismiss();
mRecentsActivity.dismissRecentsToHomeWithoutTransitionAnimation();
- // Resize all tasks beginning from the "oldest" one.
- for (int i = additionalTasks; i >= 0; --i) {
- if (mTasks[i] != null) {
- mSsp.resizeTask(mTasks[i].key.id, mBounds[i]);
+ // In debug mode, we force all task to be resizeable regardless of the
+ // current app configuration.
+ if (RecentsConfiguration.getInstance().multiStackEnabled) {
+ for (int i = additionalTasks; i >= 0; --i) {
+ if (mTasks[i] != null) {
+ mSsp.setTaskResizeable(mTasks[i].key.id);
+ }
}
}
@@ -224,7 +228,7 @@
// the focus ends on the selected one.
for (int i = additionalTasks; i >= 0; --i) {
if (mTasks[i] != null) {
- mRecentsView.launchTask(mTasks[i]);
+ mRecentsView.launchTask(mTasks[i], mBounds[i]);
}
}
}
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 89aeabc..bead1b0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -271,17 +271,12 @@
return null;
}
- /** Resize a given task. */
- public void resizeTask(int taskId, Rect bounds) {
+ /** Allow a task to resize. */
+ public void setTaskResizeable(int taskId) {
if (mIam == null) return;
try {
- if (RecentsConfiguration.getInstance().multiStackEnabled) {
- // In debug mode, we force all task to be resizeable regardless of the
- // current app configuration.
- mIam.setTaskResizeable(taskId, true);
- }
- mIam.resizeTask(taskId, bounds);
+ mIam.setTaskResizeable(taskId, true);
} catch (RemoteException e) {
e.printStackTrace();
}
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 00ac5f9..651b29a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -165,7 +165,7 @@
/** Gets the next task in the stack - or if the last - the top task */
public Task getNextTaskOrTopTask(Task taskToSearch) {
- Task returnTask = null;
+ Task returnTask = null;
boolean found = false;
List<TaskStackView> stackViews = getTaskStackViews();
int stackCount = stackViews.size();
@@ -203,7 +203,7 @@
TaskView tv = taskViews.get(j);
Task task = tv.getTask();
if (tv.isFocusedTask()) {
- onTaskViewClicked(stackView, tv, stack, task, false);
+ onTaskViewClicked(stackView, tv, stack, task, false, false, null);
return true;
}
}
@@ -212,7 +212,7 @@
}
/** Launches a given task. */
- public boolean launchTask(Task task) {
+ public boolean launchTask(Task task, Rect taskBounds) {
// Get the first stack view
List<TaskStackView> stackViews = getTaskStackViews();
int stackCount = stackViews.size();
@@ -225,7 +225,7 @@
for (int j = 0; j < taskViewCount; j++) {
TaskView tv = taskViews.get(j);
if (tv.getTask() == task) {
- onTaskViewClicked(stackView, tv, stack, task, false);
+ onTaskViewClicked(stackView, tv, stack, task, false, true, taskBounds);
return true;
}
}
@@ -250,7 +250,7 @@
if (tasks.get(j).isLaunchTarget) {
Task task = tasks.get(j);
TaskView tv = stackView.getChildViewForTask(task);
- onTaskViewClicked(stackView, tv, stack, task, false);
+ onTaskViewClicked(stackView, tv, stack, task, false, false, null);
return true;
}
}
@@ -373,7 +373,7 @@
searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
}
- // Layout each TaskStackView with the full width and height of the window since the
+ // 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();
@@ -604,7 +604,8 @@
@Override
public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
- final TaskStack stack, final Task task, final boolean lockToTask) {
+ final TaskStack stack, final Task task, final boolean lockToTask,
+ final boolean boundsValid, final Rect bounds) {
// Notify any callbacks of the launching of a new task
if (mCb != null) {
@@ -632,9 +633,9 @@
final SystemServicesProxy ssp =
RecentsTaskLoader.getInstance().getSystemServicesProxy();
ActivityOptions opts = null;
+ ActivityOptions.OnAnimationStartedListener animStartedListener = null;
if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
task.thumbnail.getHeight() > 0) {
- ActivityOptions.OnAnimationStartedListener animStartedListener = null;
if (lockToTask) {
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
boolean mTriggered = false;
@@ -665,9 +666,14 @@
offsetX, offsetY, transform.rect.width(), transform.rect.height(),
sourceView.getHandler(), animStartedListener);
}
+ } else {
+ opts = ActivityOptions.makeBasic();
}
-
+ if (boundsValid) {
+ opts.setBounds(bounds);
+ }
final ActivityOptions launchOpts = opts;
+ final boolean screenPinningRequested = (animStartedListener == null) && lockToTask;
final Runnable launchRunnable = new Runnable() {
@Override
public void run() {
@@ -677,7 +683,7 @@
} else {
if (ssp.startActivityFromRecents(getContext(), task.key.id,
task.activityLabel, launchOpts)) {
- if (launchOpts == null && lockToTask) {
+ if (screenPinningRequested) {
mCb.onScreenPinningRequest();
}
} else {
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 0068f84..4e82c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -59,7 +59,7 @@
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
- boolean lockToTask);
+ boolean lockToTask, boolean boundsValid, Rect bounds);
public void onTaskViewAppInfoClicked(Task t);
public void onTaskViewDismissed(Task t);
public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks);
@@ -1377,7 +1377,7 @@
mUIDozeTrigger.stopDozing();
if (mCb != null) {
- mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask);
+ mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask, false, null);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index de6c4b1..2c964be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1754,11 +1754,13 @@
protected void handleVisibleToUserChanged(boolean visibleToUser) {
try {
if (visibleToUser) {
- boolean clearNotificationEffects = !isPanelFullyCollapsed() &&
- (mShowLockscreenNotifications ||
- (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED));
+ boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
+ boolean clearNotificationEffects =
+ ((mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD) ||
+ (!pinnedHeadsUp && (mState == StatusBarState.SHADE
+ || mState == StatusBarState.SHADE_LOCKED)));
int notificationLoad = mNotificationData.getActiveNotifications().size();
- if (mHeadsUpManager.hasPinnedHeadsUp() && isPanelFullyCollapsed()) {
+ if (pinnedHeadsUp && isPanelFullyCollapsed()) {
notificationLoad = 1;
} else {
MetricsLogger.histogram(mContext, "note_load", notificationLoad);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java
new file mode 100644
index 0000000..f6c1062
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java
@@ -0,0 +1,60 @@
+/*
+ * 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.statusbar.phone;
+
+import android.app.ActivityManager.RecentTaskInfo;
+
+import java.util.ArrayList;
+
+/**
+ * Data associated with an app button.
+ */
+class AppButtonData {
+ public final AppInfo appInfo;
+ public boolean pinned;
+ // Recent tasks for this app, sorted by lastActiveTime, descending.
+ public ArrayList<RecentTaskInfo> tasks;
+
+ public AppButtonData(AppInfo appInfo, boolean pinned) {
+ this.appInfo = appInfo;
+ 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 && getTaskCount() == 0;
+ }
+
+ public void addTask(RecentTaskInfo task) {
+ if (tasks == null) {
+ tasks = new ArrayList<RecentTaskInfo>();
+ }
+ tasks.add(task);
+ }
+
+ public void clearTasks() {
+ if (tasks != null) {
+ tasks.clear();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java
index e34c821..8f0b532 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java
@@ -39,4 +39,16 @@
public UserHandle getUser() {
return mUser;
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final AppInfo other = (AppInfo) obj;
+ return mComponentName.equals(other.mComponentName) && mUser.equals(other.mUser);
+ }
}
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 4e69999..84082db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -227,7 +227,7 @@
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed) {
return MODE_UNLOCK;
- } else {
+ } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
return MODE_SHOW_BOUNCER;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
index f46d1a6..d2bec7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
@@ -20,6 +20,12 @@
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.RemoteException;
@@ -30,7 +36,7 @@
* Retrieves the icon for an activity and sets it as the Drawable on an ImageView. The ImageView
* is hidden if the activity isn't recognized or if there is no icon.
*/
-class GetActivityIconTask extends AsyncTask<AppInfo, Void, Drawable> {
+class GetActivityIconTask extends AsyncTask<AppButtonData, Void, Drawable> {
private final static String TAG = "GetActivityIconTask";
private final PackageManager mPackageManager;
@@ -44,11 +50,12 @@
}
@Override
- protected Drawable doInBackground(AppInfo... params) {
+ protected Drawable doInBackground(AppButtonData... params) {
if (params.length != 1) {
throw new IllegalArgumentException("Expected one parameter");
}
- AppInfo appInfo = params[0];
+ AppButtonData buttonData = params[0];
+ AppInfo appInfo = buttonData.appInfo;
try {
IPackageManager mPM = AppGlobals.getPackageManager();
ActivityInfo ai = mPM.getActivityInfo(
@@ -62,7 +69,37 @@
}
Drawable unbadgedIcon = ai.loadIcon(mPackageManager);
- return mPackageManager.getUserBadgedIcon(unbadgedIcon, appInfo.getUser());
+ Drawable badgedIcon =
+ mPackageManager.getUserBadgedIcon(unbadgedIcon, appInfo.getUser());
+
+ if (NavigationBarApps.DEBUG) {
+ // Draw pinned indicator and number of running tasks.
+ Bitmap bitmap = Bitmap.createBitmap(
+ badgedIcon.getIntrinsicWidth(),
+ badgedIcon.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ badgedIcon.setBounds(
+ 0, 0, badgedIcon.getIntrinsicWidth(), badgedIcon.getIntrinsicHeight());
+ badgedIcon.draw(canvas);
+ Paint paint = new Paint();
+ paint.setStyle(Paint.Style.FILL);
+ if (buttonData.pinned) {
+ paint.setColor(Color.WHITE);
+ canvas.drawCircle(10, 10, 10, paint);
+ }
+ if (buttonData.tasks != null && buttonData.tasks.size() > 0) {
+ paint.setColor(Color.BLACK);
+ canvas.drawCircle(60, 30, 30, paint);
+ paint.setColor(Color.WHITE);
+ paint.setTextSize(50);
+ paint.setTypeface(Typeface.create("sans-serif", Typeface.BOLD));
+ canvas.drawText(Integer.toString(buttonData.tasks.size()), 50, 50, paint);
+ }
+ badgedIcon = new BitmapDrawable(null, bitmap);
+ }
+
+ return badgedIcon;
} catch (RemoteException e) {
Slog.w(TAG, "Icon not found for " + appInfo, e);
return null;
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 d30411a..012dc9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -262,14 +262,21 @@
return (secure && !canSkipBouncer) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
}
+ /**
+ * Resolves the intent to launch the camera application.
+ */
+ public ResolveInfo resolveCameraIntent() {
+ return mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(),
+ PackageManager.MATCH_DEFAULT_ONLY,
+ KeyguardUpdateMonitor.getCurrentUser());
+ }
+
private void updateCameraVisibility() {
if (mCameraImageView == null) {
// Things are not set up yet; reply hazy, ask again later
return;
}
- ResolveInfo resolved = mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(),
- PackageManager.MATCH_DEFAULT_ONLY,
- KeyguardUpdateMonitor.getCurrentUser());
+ ResolveInfo resolved = resolveCameraIntent();
boolean visible = !isCameraDisabledByDpm() && resolved != null
&& getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance)
&& mUserSetupComplete;
@@ -383,7 +390,8 @@
serviceIntent.setAction(CameraPrewarmService.ACTION_PREWARM);
try {
if (getContext().bindServiceAsUser(serviceIntent, mPrewarmConnection,
- Context.BIND_AUTO_CREATE, new UserHandle(UserHandle.USER_CURRENT))) {
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ new UserHandle(UserHandle.USER_CURRENT))) {
mPrewarmBound = true;
}
} catch (SecurityException e) {
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 93ecb06..8e58d14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -245,10 +245,10 @@
return STATE_FINGERPRINT_ERROR;
} else if (mUnlockMethodCache.canSkipBouncer()) {
return STATE_LOCK_OPEN;
- } else if (fingerprintRunning && unlockingAllowed) {
- return STATE_FINGERPRINT;
} else if (mUnlockMethodCache.isFaceUnlockRunning()) {
return STATE_FACE_UNLOCK;
+ } else if (fingerprintRunning && unlockingAllowed) {
+ return STATE_FINGERPRINT;
} else {
return STATE_LOCKED;
}
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 04038bb..28f111f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -19,7 +19,11 @@
import android.animation.LayoutTransition;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
+import android.app.IActivityManager;
+import android.app.ITaskStackListener;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -29,18 +33,26 @@
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;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AttributeSet;
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;
@@ -51,12 +63,15 @@
/**
* Container for application icons that appear in the navigation bar. Their appearance is similar
- * to the launcher hotseat. Clicking an icon launches 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.
+ * 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 {
- private final static boolean DEBUG = false;
+class NavigationBarApps extends LinearLayout
+ implements NavigationBarAppsModel.OnAppsChangedListener {
+ public final static boolean DEBUG = false;
private final static String TAG = "NavigationBarApps";
/**
@@ -72,6 +87,7 @@
private final UserManager mUserManager;
private final LayoutInflater mLayoutInflater;
private final AppPackageMonitor mAppPackageMonitor;
+ private final WindowManager mWindowManager;
// This view has two roles:
@@ -97,13 +113,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);
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();
@@ -119,19 +153,44 @@
transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
transition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 0);
setLayoutTransition(transition);
+
+ TaskStackListener taskStackListener = new TaskStackListener();
+ IActivityManager iam = ActivityManagerNative.getDefault();
+ try {
+ iam.registerTaskStackListener(taskStackListener);
+ } 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".
private class AppPackageMonitor extends PackageMonitor {
@Override
public void onPackageRemoved(String packageName, int uid) {
- postRemoveIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
+ postUnpinIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
super.onPackageRemoved(packageName, uid);
}
@Override
public void onPackageModified(String packageName) {
- postRemoveIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
+ postUnpinIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
super.onPackageModified(packageName);
}
@@ -141,7 +200,7 @@
UserHandle user = new UserHandle(getChangingUserId());
for (String packageName : packages) {
- postRemoveIfUnlauncheable(packageName, user);
+ postUnpinIfUnlauncheable(packageName, user);
}
}
super.onPackagesAvailable(packages);
@@ -153,31 +212,36 @@
UserHandle user = new UserHandle(getChangingUserId());
for (String packageName : packages) {
- postRemoveIfUnlauncheable(packageName, user);
+ postUnpinIfUnlauncheable(packageName, user);
}
}
super.onPackagesUnavailable(packages);
}
}
- private void postRemoveIfUnlauncheable(final String packageName, final UserHandle user) {
+ private void postUnpinIfUnlauncheable(final String packageName, final UserHandle user) {
// This method doesn't necessarily get called in the main thread. Redirect the call into
// the main thread.
post(new Runnable() {
@Override
public void run() {
if (!isAttachedToWindow()) return;
- removeIfUnlauncheable(packageName, user);
+ unpinIfUnlauncheable(packageName, user);
}
});
}
- private void removeIfUnlauncheable(String packageName, UserHandle user) {
- // Remove icons for all apps that match a package that perhaps became unlauncheable.
+ private void unpinIfUnlauncheable(String packageName, UserHandle user) {
+ // Unpin icons for all apps that match a package that perhaps became unlauncheable.
+ boolean appsWereUnpinned = false;
for(int i = getChildCount() - 1; i >= 0; --i) {
View child = getChildAt(i);
- AppInfo appInfo = (AppInfo)child.getTag();
- if (appInfo == null) continue; // Skip the drag placeholder.
+ AppButtonData appButtonData = (AppButtonData)child.getTag();
+ if (appButtonData == null) continue; // Skip the drag placeholder.
+
+ if (!appButtonData.pinned) continue;
+
+ AppInfo appInfo = appButtonData.appInfo;
if (!appInfo.getUser().equals(user)) continue;
ComponentName appComponentName = appInfo.getComponentName();
@@ -187,10 +251,15 @@
continue;
}
- removeViewAt(i);
+ appButtonData.pinned = false;
+ appsWereUnpinned = true;
+
+ if (appButtonData.isEmpty()) {
+ removeViewAt(i);
+ }
}
- if (getChildCount() != sAppsModel.getApps().size()) {
- saveApps();
+ if (appsWereUnpinned) {
+ savePinnedApps();
}
}
@@ -210,7 +279,8 @@
parent.setLayoutTransition(transition);
sAppsModel.setCurrentUser(ActivityManager.getCurrentUser());
- recreateAppButtons();
+ recreatePinnedAppButtons();
+ updateRecentApps();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
@@ -218,6 +288,7 @@
mContext.registerReceiver(mBroadcastReceiver, filter);
mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+ sAppsModel.addOnAppsChangedListener(this);
}
@Override
@@ -225,13 +296,48 @@
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);
+ 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;
}
/**
* Creates an ImageView icon for each pinned app. Removes any existing icons. May be called
* to synchronize the current view with the shared data mode.
*/
- public void recreateAppButtons() {
+ private void recreatePinnedAppButtons() {
// Remove any existing icon buttons.
removeAllViews();
@@ -239,54 +345,39 @@
int appCount = apps.size();
for (int i = 0; i < appCount; i++) {
AppInfo app = apps.get(i);
- ImageView button = createAppButton(app);
- addView(button);
-
- CharSequence appLabel = getAppLabel(mPackageManager, app.getComponentName());
- button.setContentDescription(appLabel);
-
- // Load the icon asynchronously.
- new GetActivityIconTask(mPackageManager, button).execute(app);
+ addAppButton(new AppButtonData(app, true /* pinned */));
}
}
/**
- * Saves apps stored in app icons into the data model.
+ * Saves pinned apps stored in app icons into the data model.
*/
- private void saveApps() {
- List<AppInfo> apps = new ArrayList<AppInfo>();
- int childCount = getChildCount();
- for (int i = 0; i != childCount; ++i) {
- View child = getChildAt(i);
- AppInfo appInfo = (AppInfo)child.getTag();
- if (appInfo == null) continue; // Skip the drag placeholder.
- apps.add(appInfo);
- }
- sAppsModel.setApps(apps);
+ private void savePinnedApps() {
+ sAppsModel.setApps(getPinnedApps());
}
/**
- * Creates a new ImageView for a launcher activity, inflated from
- * R.layout.navigation_bar_app_item.
+ * Creates a new ImageView for an app, inflated from R.layout.navigation_bar_app_item.
*/
- private ImageView createAppButton(AppInfo appInfo) {
+ private ImageView createAppButton(AppButtonData appButtonData) {
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(appInfo);
+ button.setTag(appButtonData);
return button;
}
- // Not shared with NavigationBarRecents because the data model is specific to pinned apps.
private class AppLongClickListener implements View.OnLongClickListener {
@Override
public boolean onLongClick(View v) {
mDragView = (ImageView) v;
- AppInfo app = (AppInfo) v.getTag();
- startAppDrag(mDragView, app);
+ AppButtonData appButtonData = (AppButtonData) v.getTag();
+ startAppDrag(mDragView, appButtonData.appInfo);
return true;
}
}
@@ -399,17 +490,48 @@
}
/**
+ * 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;
}
@@ -419,7 +541,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.
@@ -438,30 +581,30 @@
return true;
}
+ boolean dragResult = true;
AppInfo appInfo = getAppFromDragEvent(event);
if (appInfo == null) {
// This wasn't a valid drop. Clean up the placeholder.
removePlaceholderDragViewIfNeeded();
- return false;
+ dragResult = false;
+ } else if (mDragView.getTag() == null) {
+ // This is a drag that adds a new app. Convert the placeholder to a real icon.
+ updateApp(mDragView, new AppButtonData(appInfo, true /* pinned */));
}
-
- // If this was an existing app being dragged then end the drag.
- if (mDragView.getTag() != null) {
- endDrag();
- return true;
- }
-
- // The drop had valid data. Convert the placeholder to a real icon.
- updateApp(mDragView, appInfo);
endDrag();
- return true;
+ return dragResult;
}
/** Cleans up at the end of a drag. */
private void endDrag() {
+ // An earlier drag event might have canceled the drag. If so, there is nothing to do.
+ if (mDragView == null) return;
+
mDragView.setVisibility(View.VISIBLE);
mDragView = null;
- saveApps();
+ savePinnedApps();
+ // Add recent tasks to the info of the potentially added app.
+ updateRecentApps();
}
/** Returns an app info from a DragEvent, or null if the data wasn't valid. */
@@ -501,9 +644,9 @@
}
/** Updates the app at a given view index. */
- private void updateApp(ImageView button, AppInfo appInfo) {
- button.setTag(appInfo);
- new GetActivityIconTask(mPackageManager, button).execute(appInfo);
+ private void updateApp(ImageView button, AppButtonData appButtonData) {
+ button.setTag(appButtonData);
+ new GetActivityIconTask(mPackageManager, button).execute(appButtonData);
}
/** Removes the empty placeholder view. */
@@ -513,7 +656,6 @@
return;
}
removeView(mDragView);
- endDrag();
}
/** Cleans up at the end of the drag. */
@@ -521,6 +663,7 @@
if (DEBUG) Slog.d(TAG, "onDragEnded");
// If the icon wasn't already dropped into the app list then remove the placeholder.
removePlaceholderDragViewIfNeeded();
+ endDrag();
return true;
}
@@ -553,12 +696,139 @@
}
/**
+ * 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 {
- @Override
- public void onClick(View v) {
- AppInfo appInfo = (AppInfo)v.getTag();
+ private void launchApp(AppInfo appInfo, View anchor) {
Intent launchIntent = sAppsModel.buildAppLaunchIntent(appInfo);
if (launchIntent == null) {
Toast.makeText(
@@ -571,32 +841,281 @@
// already open in a visible window. In that case we should move the task to front
// with minimal animation, perhaps using ActivityManager.moveTaskToFront().
Rect sourceBounds = new Rect();
- v.getBoundsOnScreen(sourceBounds);
+ anchor.getBoundsOnScreen(sourceBounds);
ActivityOptions opts =
- ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getWidth(), v.getHeight());
+ ActivityOptions.makeScaleUpAnimation(
+ anchor, 0, 0, anchor.getWidth(), anchor.getHeight());
Bundle optsBundle = opts.toBundle();
launchIntent.setSourceBounds(sourceBounds);
mContext.startActivityAsUser(launchIntent, optsBundle, appInfo.getUser());
}
+
+ @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 boolean onContextClick(View v) {
+ if (mIsPopupInUse) return true;
+ ImageView appIcon = (ImageView) v;
+ populateContextMenu(appIcon);
+ showPopupMenu(appIcon);
+ return true;
+ }
}
private void onUserSwitched(int currentUserId) {
sAppsModel.setCurrentUser(currentUserId);
- recreateAppButtons();
+ recreatePinnedAppButtons();
}
private void onManagedProfileRemoved(UserHandle removedProfile) {
+ // Unpin apps from the removed profile.
+ boolean itemsWereUnpinned = false;
for(int i = getChildCount() - 1; i >= 0; --i) {
View view = getChildAt(i);
- AppInfo appInfo = (AppInfo)view.getTag();
- if (appInfo == null) return; // Skip the drag placeholder.
- if (!appInfo.getUser().equals(removedProfile)) continue;
+ AppButtonData appButtonData = (AppButtonData)view.getTag();
+ if (appButtonData == null) return; // Skip the drag placeholder.
+ if (!appButtonData.pinned) continue;
+ if (!appButtonData.appInfo.getUser().equals(removedProfile)) continue;
- removeViewAt(i);
+ appButtonData.pinned = false;
+ itemsWereUnpinned = true;
+ if (appButtonData.isEmpty()) {
+ removeViewAt(i);
+ }
}
- if (getChildCount() != sAppsModel.getApps().size()) {
- saveApps();
+ if (itemsWereUnpinned) {
+ savePinnedApps();
}
}
+
+ /**
+ * Returns app data for a button that matches the provided app info, if it exists, or null
+ * otherwise.
+ */
+ private AppButtonData findAppButtonData(AppInfo appInfo) {
+ int size = getChildCount();
+ for (int i = 0; i < size; ++i) {
+ View view = getChildAt(i);
+ AppButtonData appButtonData = (AppButtonData)view.getTag();
+ if (appButtonData == null) continue; // Skip the drag placeholder.
+ if (appButtonData.appInfo.equals(appInfo)) {
+ return appButtonData;
+ }
+ }
+ return null;
+ }
+
+ private void updateTasks(List<RecentTaskInfo> tasks) {
+ // Remove tasks from all app buttons.
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ View view = getChildAt(i);
+ AppButtonData appButtonData = (AppButtonData)view.getTag();
+ if (appButtonData == null) return; // Skip the drag placeholder.
+ appButtonData.clearTasks();
+ }
+
+ // Re-add tasks to app buttons, adding new buttons if needed.
+ int size = tasks.size();
+ for (int i = 0; i != size; ++i) {
+ RecentTaskInfo task = tasks.get(i);
+ AppInfo taskAppInfo = taskToAppInfo(task);
+ if (taskAppInfo == null) continue;
+ AppButtonData appButtonData = findAppButtonData(taskAppInfo);
+ if (appButtonData == null) {
+ appButtonData = new AppButtonData(taskAppInfo, false);
+ addAppButton(appButtonData);
+ }
+ appButtonData.addTask(task);
+ }
+
+ // Remove unpinned apps that now have no tasks.
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ View view = getChildAt(i);
+ AppButtonData appButtonData = (AppButtonData)view.getTag();
+ if (appButtonData == null) return; // Skip the drag placeholder.
+ if (appButtonData.isEmpty()) {
+ removeViewAt(i);
+ }
+ }
+
+ if (DEBUG) {
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ View view = getChildAt(i);
+ AppButtonData appButtonData = (AppButtonData)view.getTag();
+ if (appButtonData == null) return; // Skip the drag placeholder.
+ new GetActivityIconTask(mPackageManager, (ImageView )view).execute(appButtonData);
+
+ }
+ }
+ }
+
+ private void updateRecentApps() {
+ ActivityManager activityManager =
+ (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ // TODO: Should this be getRunningTasks?
+ List<RecentTaskInfo> recentTasks = activityManager.getRecentTasksForUser(
+ ActivityManager.getMaxAppRecentsLimitStatic(),
+ ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
+ ActivityManager.RECENT_IGNORE_UNAVAILABLE |
+ ActivityManager.RECENT_INCLUDE_PROFILES,
+ UserHandle.USER_CURRENT);
+ if (DEBUG) Slog.d(TAG, "Got recents " + recentTasks.size());
+ updateTasks(recentTasks);
+ }
+
+ private static ComponentName getActivityForTask(RecentTaskInfo task) {
+ // If the task was started from an alias, return the actual activity component that was
+ // initially started.
+ if (task.origActivity != null) {
+ return task.origActivity;
+ }
+ // Prefer the first activity of the task.
+ if (task.baseActivity != null) {
+ return task.baseActivity;
+ }
+ // Then goes the activity that started the task.
+ if (task.realActivity != null) {
+ return task.realActivity;
+ }
+ // This should not happen, but fall back to the base intent's activity component name.
+ return task.baseIntent.getComponent();
+ }
+
+ private ComponentName getLaunchComponentForPackage(String packageName, int userId) {
+ // This code is based on ApplicationPackageManager.getLaunchIntentForPackage.
+ PackageManager packageManager = mContext.getPackageManager();
+
+ // First see if the package has an INFO activity; the existence of
+ // such an activity is implied to be the desired front-door for the
+ // overall package (such as if it has multiple launcher entries).
+ Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+ intentToResolve.addCategory(Intent.CATEGORY_INFO);
+ intentToResolve.setPackage(packageName);
+ List<ResolveInfo> ris = packageManager.queryIntentActivitiesAsUser(
+ intentToResolve, 0, userId);
+
+ // Otherwise, try to find a main launcher activity.
+ if (ris == null || ris.size() <= 0) {
+ // reuse the intent instance
+ intentToResolve.removeCategory(Intent.CATEGORY_INFO);
+ intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+ intentToResolve.setPackage(packageName);
+ ris = packageManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId);
+ }
+ if (ris == null || ris.size() <= 0) {
+ Slog.e(TAG, "Failed to build intent for " + packageName);
+ return null;
+ }
+ return new ComponentName(ris.get(0).activityInfo.packageName,
+ ris.get(0).activityInfo.name);
+ }
+
+ private AppInfo taskToAppInfo(RecentTaskInfo task) {
+ ComponentName componentName = getActivityForTask(task);
+ UserHandle taskUser = new UserHandle(task.userId);
+ AppInfo appInfo = new AppInfo(componentName, taskUser);
+
+ if (sAppsModel.buildAppLaunchIntent(appInfo) == null) {
+ // If task's activity is not launcheable, fall back to a launch component of the
+ // task's package.
+ ComponentName component = getLaunchComponentForPackage(
+ componentName.getPackageName(), task.userId);
+
+ if (component == null) {
+ return null;
+ }
+
+ appInfo = new AppInfo(component, taskUser);
+ }
+
+ return appInfo;
+ }
+
+ /**
+ * A listener that updates the app buttons whenever the recents task stack changes.
+ */
+ private class TaskStackListener extends ITaskStackListener.Stub {
+ @Override
+ public void onTaskStackChanged() throws RemoteException {
+ // Post the message back to the UI thread.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (isAttachedToWindow()) {
+ updateRecentApps();
+ }
+ }
+ });
+ }
+ }
+
+ @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..badb846 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,10 @@
* ComponentName.
*/
class NavigationBarAppsModel {
+ public interface OnAppsChangedListener {
+ void onPinnedAppsChanged();
+ }
+
private final static String TAG = "NavigationBarAppsModel";
// Default number of apps to load initially.
@@ -79,6 +83,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;
@@ -167,6 +174,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 +254,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. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java
deleted file mode 100644
index 3bfa4d5..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java
+++ /dev/null
@@ -1,273 +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.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo;
-import android.app.ActivityManagerNative;
-import android.app.IActivityManager;
-import android.app.ITaskStackListener;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.systemui.R;
-
-import java.util.List;
-
-/**
- * Recent task icons appearing in the navigation bar. Touching an icon brings the activity to the
- * front. The tag for each icon's View contains the RecentTaskInfo.
- */
-class NavigationBarRecents extends LinearLayout {
- private final static boolean DEBUG = false;
- private final static String TAG = "NavigationBarRecents";
-
- // Maximum number of icons to show.
- // TODO: Implement an overflow UI so the shelf can display an unlimited number of recents.
- private final static int MAX_RECENTS = 10;
-
- private final ActivityManager mActivityManager;
- private final PackageManager mPackageManager;
- private final LayoutInflater mLayoutInflater;
- // All icons share the same long-click listener.
- private final AppLongClickListener mAppLongClickListener;
- private final TaskStackListenerImpl mTaskStackListener;
-
- public NavigationBarRecents(Context context, AttributeSet attrs) {
- super(context, attrs);
- mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- mPackageManager = getContext().getPackageManager();
- mLayoutInflater = LayoutInflater.from(context);
- mAppLongClickListener = new AppLongClickListener(context);
-
- // Listen for task stack changes and refresh when they happen. Update notifications happen
- // on an IPC thread, so use Handler to handle the message on the main thread.
- // TODO: This has too much latency. It only adds the icon when app launch is completed
- // and the launch animation is done playing. This class should add the icon immediately
- // when the launch starts.
- Handler handler = new Handler();
- mTaskStackListener = new TaskStackListenerImpl(handler);
- IActivityManager iam = ActivityManagerNative.getDefault();
- try {
- iam.registerTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- private void updateRecentApps() {
- // TODO: Should this be getRunningTasks?
- // TODO: Query other UserHandles?
- List<RecentTaskInfo> recentTasks = mActivityManager.getRecentTasksForUser(
- MAX_RECENTS,
- ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
- ActivityManager.RECENT_IGNORE_UNAVAILABLE |
- ActivityManager.RECENT_INCLUDE_PROFILES,
- UserHandle.USER_CURRENT);
- if (DEBUG) Slog.d(TAG, "Got recents " + recentTasks.size());
- removeMissingRecents(recentTasks);
- addNewRecents(recentTasks);
- }
-
- // Removes any icons that disappeared from recents.
- private void removeMissingRecents(List<RecentTaskInfo> recentTasks) {
- // Build a set of the new task ids.
- SparseBooleanArray newTaskIds = new SparseBooleanArray();
- for (RecentTaskInfo task : recentTasks) {
- newTaskIds.put(task.persistentId, true);
- }
-
- // Iterate through the currently displayed tasks. If they no longer exist in recents,
- // remove them.
- int i = 0;
- while (i < getChildCount()) {
- RecentTaskInfo currentTask = (RecentTaskInfo) getChildAt(i).getTag();
- if (!newTaskIds.get(currentTask.persistentId)) {
- if (DEBUG) Slog.d(TAG, "Removing " + currentTask.baseIntent);
- removeViewAt(i);
- } else {
- i++;
- }
- }
- }
-
- // Adds new tasks at the end of the icon list.
- private void addNewRecents(List<RecentTaskInfo> recentTasks) {
- // Build a set of the current task ids.
- SparseBooleanArray currentTaskIds = new SparseBooleanArray();
- for (int i = 0; i < getChildCount(); i++) {
- RecentTaskInfo task = (RecentTaskInfo) getChildAt(i).getTag();
- currentTaskIds.put(task.persistentId, true);
- }
-
- // Add tasks that don't currently exist to the end of the view.
- for (RecentTaskInfo task : recentTasks) {
- // Don't overflow the list.
- if (getChildCount() >= MAX_RECENTS) {
- return;
- }
- // Don't add tasks that are already being shown.
- if (currentTaskIds.get(task.persistentId)) {
- continue;
- }
- addRecentAppButton(task);
- }
- }
-
- // Adds an icon at the end of the shelf.
- private void addRecentAppButton(RecentTaskInfo task) {
- if (DEBUG) Slog.d(TAG, "Adding " + task.baseIntent);
-
- // Add an icon for the task.
- ImageView button = (ImageView) mLayoutInflater.inflate(
- R.layout.navigation_bar_app_item, this, false /* attachToRoot */);
- button.setOnLongClickListener(mAppLongClickListener);
- addView(button);
-
- ComponentName activityName = getRealActivityForTask(task);
- CharSequence appLabel = NavigationBarApps.getAppLabel(mPackageManager, activityName);
- button.setContentDescription(appLabel);
-
- // Use the View's tag to store metadata for drag and drop.
- button.setTag(task);
-
- button.setVisibility(View.VISIBLE);
- // Load the activity icon on a background thread.
- AppInfo app = new AppInfo(activityName, new UserHandle(task.userId));
- new GetActivityIconTask(mPackageManager, button).execute(app);
-
- final int taskPersistentId = task.persistentId;
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // 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);
- }
- }
- });
- }
-
- private static ComponentName getRealActivityForTask(RecentTaskInfo task) {
- // Prefer the activity that started the task.
- if (task.realActivity != null) {
- return task.realActivity;
- }
- // This should not happen, but fall back to the base intent's activity component name.
- return task.baseIntent.getComponent();
- }
-
- /**
- * A listener that updates the app buttons whenever the recents task stack changes.
- * NOTE: This is not the right way to do this.
- */
- private class TaskStackListenerImpl extends ITaskStackListener.Stub {
- // Handler to post messages to the UI thread.
- private Handler mHandler;
-
- public TaskStackListenerImpl(Handler handler) {
- mHandler = handler;
- }
-
- @Override
- public void onTaskStackChanged() throws RemoteException {
- // Post the message back to the UI thread.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- updateRecentApps();
- }
- });
- }
- }
-
- /** Starts a drag on long-click on an app icon. */
- private static class AppLongClickListener implements View.OnLongClickListener {
- private final Context mContext;
-
- public AppLongClickListener(Context context) {
- mContext = context;
- }
-
- private ComponentName getLaunchComponentForPackage(String packageName, int userId) {
- // This code is based on ApplicationPackageManager.getLaunchIntentForPackage.
- PackageManager packageManager = mContext.getPackageManager();
-
- // First see if the package has an INFO activity; the existence of
- // such an activity is implied to be the desired front-door for the
- // overall package (such as if it has multiple launcher entries).
- Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
- intentToResolve.addCategory(Intent.CATEGORY_INFO);
- intentToResolve.setPackage(packageName);
- List<ResolveInfo> ris = packageManager.queryIntentActivitiesAsUser(
- intentToResolve, 0, userId);
-
- // Otherwise, try to find a main launcher activity.
- if (ris == null || ris.size() <= 0) {
- // reuse the intent instance
- intentToResolve.removeCategory(Intent.CATEGORY_INFO);
- intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
- intentToResolve.setPackage(packageName);
- ris = packageManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId);
- }
- if (ris == null || ris.size() <= 0) {
- Slog.e(TAG, "Failed to build intent for " + packageName);
- return null;
- }
- return new ComponentName(ris.get(0).activityInfo.packageName,
- ris.get(0).activityInfo.name);
- }
-
- @Override
- public boolean onLongClick(View v) {
- ImageView icon = (ImageView) v;
-
- // The drag will go to the pinned section, which wants to launch the main activity
- // for the task's package.
- RecentTaskInfo task = (RecentTaskInfo) v.getTag();
- String packageName = getRealActivityForTask(task).getPackageName();
- ComponentName component = getLaunchComponentForPackage(packageName, task.userId);
- if (component == null) {
- return false;
- }
-
- if (DEBUG) Slog.d(TAG, "Start drag with " + component);
-
- NavigationBarApps.startAppDrag(
- icon, new AppInfo(component, new UserHandle(task.userId)));
- return true;
- }
- }
-}
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 7de7a7b..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
@@ -492,14 +492,6 @@
}
updateTaskSwitchHelper();
-
- // If using the app shelf, synchronize the current icons to the data model.
- NavigationBarApps apps =
- (NavigationBarApps) mCurrentView.findViewById(R.id.navigation_bar_apps);
- if (apps != null) {
- apps.recreateAppButtons();
- }
-
setNavigationIconHints(mNavigationIconHints, true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 950b162..310625e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -78,6 +78,13 @@
Notification notif = sbn.getNotification();
String groupKey = sbn.getGroupKey();
final NotificationGroup group = mGroupMap.get(groupKey);
+ if (group == null) {
+ // When an app posts 2 different notifications as summary of the same group, then a
+ // cancellation of the first notification removes this group.
+ // This situation is not supported and we will not allow such notifications anymore in
+ // the close future. See b/23676310 for reference.
+ return;
+ }
if (notif.isGroupSummary()) {
group.summary = null;
} else {
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 3ad7246..f47ec20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -21,7 +21,10 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -62,6 +65,8 @@
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
+import java.util.List;
+
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
@@ -107,12 +112,6 @@
private boolean mQsTracking;
/**
- * Handles launching the secure camera properly even when other applications may be using the
- * camera hardware.
- */
- private SecureCameraLaunchManager mSecureCameraLaunchManager;
-
- /**
* If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
* the expansion for quick settings.
*/
@@ -260,8 +259,6 @@
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
- mSecureCameraLaunchManager =
- new SecureCameraLaunchManager(getContext(), mKeyguardBottomArea);
mLastOrientation = getResources().getConfiguration().orientation;
// recompute internal state when qspanel height changes
@@ -366,16 +363,6 @@
updateMaxHeadsUpTranslation();
}
- @Override
- public void onAttachedToWindow() {
- mSecureCameraLaunchManager.create();
- }
-
- @Override
- public void onDetachedFromWindow() {
- mSecureCameraLaunchManager.destroy();
- }
-
private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
if (mQsSizeChangeAnimator != null) {
oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
@@ -1987,12 +1974,12 @@
mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
@Override
public void run() {
- mSecureCameraLaunchManager.startSecureCameraLaunch();
+ mKeyguardBottomArea.launchCamera();
}
}, null, true /* dismissShade */, false /* afterKeyguardGone */);
}
else {
- mSecureCameraLaunchManager.startSecureCameraLaunch();
+ mKeyguardBottomArea.launchCamera();
}
}
mStatusBar.startLaunchTransitionTimeout();
@@ -2041,7 +2028,6 @@
boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon
: rightIcon;
if (camera) {
- mSecureCameraLaunchManager.onSwipingStarted();
mKeyguardBottomArea.bindCameraPrewarmService();
}
requestDisallowInterceptTouchEvent(true);
@@ -2462,7 +2448,28 @@
getCenterIcon().setLaunchingAffordance(launchingAffordance);
}
- public boolean canCameraGestureBeLaunched() {
- return !mAfforanceHelper.isSwipingInProgress();
+ /**
+ * Whether the camera application can be launched for the camera launch gesture.
+ *
+ * @param keyguardIsShowing whether keyguard is being shown
+ */
+ public boolean canCameraGestureBeLaunched(boolean keyguardIsShowing) {
+ ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
+ String packageToLaunch = (resolveInfo == null || resolveInfo.activityInfo == null)
+ ? null : resolveInfo.activityInfo.packageName;
+ return packageToLaunch != null &&
+ (keyguardIsShowing || !isForegroundApp(packageToLaunch)) &&
+ !mAfforanceHelper.isSwipingInProgress();
+ }
+
+ /**
+ * Return true if the applications with the package name is running in foreground.
+ *
+ * @param pkgName application package name.
+ */
+ private boolean isForegroundApp(String pkgName) {
+ ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
+ return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
}
}
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 7b04da0..ea6681e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -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,18 @@
private boolean mLaunchTransitionFadingAway;
private ExpandableNotificationRow mDraggedDownRow;
private boolean mLaunchCameraOnScreenTurningOn;
+ private boolean mLaunchCameraOnFinishedGoingToSleep;
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;
@@ -2792,9 +2802,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 +3037,8 @@
updateNotifications();
resetUserSetupObserver();
setControllerUsers();
- mAssistManager.onUserSwitched(newUserId);
+ clearCurrentMediaNotification();
+ updateMediaMetaData(true);
if (mFullscreenUserSwitcher != null) {
mFullscreenUserSwitcher.onUserSwitched(newUserId);
}
@@ -3575,6 +3583,7 @@
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
releaseGestureWakeLock();
mNotificationPanel.onAffordanceLaunchEnded();
+ mNotificationPanel.animate().cancel();
mNotificationPanel.setAlpha(1f);
return staying;
}
@@ -3596,6 +3605,7 @@
// Treat Keyguard exit animation as an app transition to achieve nice transition for status
// bar.
+ mKeyguardGoingAway = true;
mIconController.appTransitionPending();
}
@@ -3627,6 +3637,7 @@
*/
public void finishKeyguardFadingAway() {
mKeyguardFadingAway = false;
+ mKeyguardGoingAway = false;
}
public void stopWaitingForKeyguardExit() {
@@ -3794,12 +3805,9 @@
// down on the lockscreen), clear notification LED, vibration,
// ringing.
// Other transitions are covered in handleVisibleToUserChanged().
- if (state != mState && mVisible && state == StatusBarState.SHADE_LOCKED) {
- try {
- mBarService.clearNotificationEffects();
- } catch (RemoteException e) {
- // Ignore.
- }
+ if (state != mState && mVisible && (state == StatusBarState.SHADE_LOCKED
+ || (state == StatusBarState.SHADE && isGoingToNotificationShade()))) {
+ clearNotificationEffects();
}
mState = state;
mGroupManager.setStatusBarState(state);
@@ -3961,16 +3969,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();
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();
+ }
+ });
+ }
}
public void onStartedWakingUp() {
@@ -3982,6 +4007,7 @@
}
public void onScreenTurningOn() {
+ mScreenTurningOn = true;
mNotificationPanel.onScreenTurningOn();
if (mLaunchCameraOnScreenTurningOn) {
mNotificationPanel.launchCamera(false);
@@ -3990,10 +4016,12 @@
}
private void vibrateForCameraGesture() {
- mVibrator.vibrate(1000L);
+ // 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();
}
@@ -4138,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) {
@@ -4150,7 +4177,12 @@
@Override
public void onCameraLaunchGestureDetected() {
- if (!mNotificationPanel.canCameraGestureBeLaunched()) {
+ if (mStartedGoingToSleep) {
+ mLaunchCameraOnFinishedGoingToSleep = true;
+ return;
+ }
+ if (!mNotificationPanel.canCameraGestureBeLaunched(
+ mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
return;
}
if (!mDeviceInteractive) {
@@ -4169,7 +4201,7 @@
mScrimController.dontAnimateBouncerChangesUntilNextFrame();
mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
- if (mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
+ if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
mNotificationPanel.launchCamera(mDeviceInteractive /* animate */);
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 12434ac..385c5d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -36,6 +36,11 @@
import com.android.systemui.qs.tiles.HotspotTile;
import com.android.systemui.qs.tiles.IntentTile;
import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.QAirplaneTile;
+import com.android.systemui.qs.tiles.QBluetoothTile;
+import com.android.systemui.qs.tiles.QFlashlightTile;
+import com.android.systemui.qs.tiles.QRotationLockTile;
+import com.android.systemui.qs.tiles.QWifiTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.WifiTile;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -64,7 +69,7 @@
private static final String TAG = "QSTileHost";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- protected static final String TILES_SETTING = "sysui_qs_tiles";
+ public static final String TILES_SETTING = "sysui_qs_tiles";
private final Context mContext;
private final PhoneStatusBar mStatusBar;
@@ -243,8 +248,8 @@
}
protected QSTile<?> createTile(String tileSpec) {
- if (tileSpec.equals("wifi")) return new WifiTile(this);
- else if (tileSpec.equals("bt")) return new BluetoothTile(this);
+ if (tileSpec.equals("wifi")) return new WifiTile(this, false);
+ else if (tileSpec.equals("bt")) return new BluetoothTile(this, false);
else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
else if (tileSpec.equals("cell")) return new CellularTile(this);
else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this);
@@ -254,6 +259,16 @@
else if (tileSpec.equals("location")) return new LocationTile(this);
else if (tileSpec.equals("cast")) return new CastTile(this);
else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
+ // Detail only versions of wifi and bluetooth.
+ else if (tileSpec.equals("dwifi")) return new WifiTile(this, true);
+ else if (tileSpec.equals("dbt")) return new BluetoothTile(this, true);
+ // Quick tiles, no text.
+ else if (tileSpec.equals("qwifi")) return new QWifiTile(this);
+ else if (tileSpec.equals("qbt")) return new QBluetoothTile(this);
+ else if (tileSpec.equals("qairplane")) return new QAirplaneTile(this);
+ else if (tileSpec.equals("qrotation")) return new QRotationLockTile(this);
+ else if (tileSpec.equals("qflashlight")) return new QFlashlightTile(this);
+ // Intent tiles.
else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java
deleted file mode 100644
index 45c8938..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java
+++ /dev/null
@@ -1,284 +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.statusbar.phone;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.hardware.camera2.CameraManager;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.provider.MediaStore;
-import android.util.Log;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Handles launching the secure camera properly even when other applications may be using the camera
- * hardware.
- *
- * When other applications (e.g., Face Unlock) are using the camera, they must close the camera to
- * allow the secure camera to open it. Since we want to minimize the delay when opening the secure
- * camera, other apps should close the camera at the first possible opportunity (i.e., as soon as
- * the user begins swiping to go to the secure camera).
- *
- * If the camera is unavailable when the user begins to swipe, the SecureCameraLaunchManager sends a
- * broadcast to tell other apps to close the camera. When and if the user completes their swipe to
- * launch the secure camera, the SecureCameraLaunchManager delays launching the secure camera until
- * a callback indicates that the camera has become available. If it doesn't receive that callback
- * within a specified timeout period, the secure camera is launched anyway.
- *
- * Ideally, the secure camera would handle waiting for the camera to become available. This allows
- * some of the time necessary to close the camera to happen in parallel with starting the secure
- * camera app. We can't rely on all third-party camera apps to handle this. However, an app can
- * put com.android.systemui.statusbar.phone.will_wait_for_camera_available in its meta-data to
- * indicate that it will be responsible for waiting for the camera to become available.
- *
- * It is assumed that the functions in this class, including the constructor, will be called from
- * the UI thread.
- */
-public class SecureCameraLaunchManager {
- private static final boolean DEBUG = false;
- private static final String TAG = "SecureCameraLaunchManager";
-
- // Action sent as a broadcast to tell other apps to stop using the camera. Other apps that use
- // the camera from keyguard (e.g., Face Unlock) should listen for this broadcast and close the
- // camera as soon as possible after receiving it.
- private static final String CLOSE_CAMERA_ACTION_NAME =
- "com.android.systemui.statusbar.phone.CLOSE_CAMERA";
-
- // Apps should put this field in their meta-data to indicate that they will take on the
- // responsibility of waiting for the camera to become available. If this field is present, the
- // SecureCameraLaunchManager launches the secure camera even if the camera hardware has not
- // become available. Having the secure camera app do the waiting is the optimal approach, but
- // without this field, the SecureCameraLaunchManager doesn't launch the secure camera until the
- // camera hardware is available.
- private static final String META_DATA_WILL_WAIT_FOR_CAMERA_AVAILABLE =
- "com.android.systemui.statusbar.phone.will_wait_for_camera_available";
-
- // If the camera hardware hasn't become available after this period of time, the
- // SecureCameraLaunchManager launches the secure camera anyway.
- private static final int CAMERA_AVAILABILITY_TIMEOUT_MS = 1000;
-
- private Context mContext;
- private Handler mHandler;
- private LockPatternUtils mLockPatternUtils;
- private KeyguardBottomAreaView mKeyguardBottomArea;
-
- private CameraManager mCameraManager;
- private CameraAvailabilityCallback mCameraAvailabilityCallback;
- private Map<String, Boolean> mCameraAvailabilityMap;
- private boolean mWaitingToLaunchSecureCamera;
- private Runnable mLaunchCameraRunnable;
-
- private class CameraAvailabilityCallback extends CameraManager.AvailabilityCallback {
- @Override
- public void onCameraUnavailable(String cameraId) {
- if (DEBUG) Log.d(TAG, "onCameraUnavailble(" + cameraId + ")");
- mCameraAvailabilityMap.put(cameraId, false);
- }
-
- @Override
- public void onCameraAvailable(String cameraId) {
- if (DEBUG) Log.d(TAG, "onCameraAvailable(" + cameraId + ")");
- mCameraAvailabilityMap.put(cameraId, true);
-
- // If we were waiting for the camera hardware to become available to launch the
- // secure camera, we can launch it now if all cameras are available. If one or more
- // cameras are still not available, we will get this callback again for those
- // cameras.
- if (mWaitingToLaunchSecureCamera && areAllCamerasAvailable()) {
- mKeyguardBottomArea.launchCamera();
- mWaitingToLaunchSecureCamera = false;
-
- // We no longer need to launch the camera after the timeout hits.
- mHandler.removeCallbacks(mLaunchCameraRunnable);
- }
- }
- }
-
- public SecureCameraLaunchManager(Context context, KeyguardBottomAreaView keyguardBottomArea) {
- mContext = context;
- mHandler = new Handler();
- mLockPatternUtils = new LockPatternUtils(context);
- mKeyguardBottomArea = keyguardBottomArea;
-
- mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
- mCameraAvailabilityCallback = new CameraAvailabilityCallback();
-
- // An onCameraAvailable() or onCameraUnavailable() callback will be received for each camera
- // when the availability callback is registered, thus initializing the map.
- //
- // Keeping track of the state of all cameras using the onCameraAvailable() and
- // onCameraUnavailable() callbacks can get messy when dealing with hot-pluggable cameras.
- // However, we have a timeout in place such that we will never hang waiting for cameras.
- mCameraAvailabilityMap = new HashMap<String, Boolean>();
-
- mWaitingToLaunchSecureCamera = false;
- mLaunchCameraRunnable = new Runnable() {
- @Override
- public void run() {
- if (mWaitingToLaunchSecureCamera) {
- Log.w(TAG, "Timeout waiting for camera availability");
- mKeyguardBottomArea.launchCamera();
- mWaitingToLaunchSecureCamera = false;
- }
- }
- };
- }
-
- /**
- * Initializes the SecureCameraManager and starts listening for camera availability.
- */
- public void create() {
- mCameraManager.registerAvailabilityCallback(mCameraAvailabilityCallback, mHandler);
- }
-
- /**
- * Stops listening for camera availability and cleans up the SecureCameraManager.
- */
- public void destroy() {
- mCameraManager.unregisterAvailabilityCallback(mCameraAvailabilityCallback);
- }
-
- /**
- * Called when the user is starting to swipe horizontally, possibly to start the secure camera.
- * Although this swipe ultimately may not result in the secure camera opening, we need to stop
- * all other camera usage (e.g., Face Unlock) as soon as possible. We send out a broadcast to
- * notify other apps that they should close the camera immediately. The broadcast is sent even
- * if the camera appears to be available, because there could be an app that is about to open
- * the camera.
- */
- public void onSwipingStarted() {
- if (DEBUG) Log.d(TAG, "onSwipingStarted");
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- Intent intent = new Intent();
- intent.setAction(CLOSE_CAMERA_ACTION_NAME);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
- }
- });
- }
-
- /**
- * Called when the secure camera should be started. If the camera is available or the secure
- * camera app has indicated that it will wait for camera availability, the secure camera app is
- * launched immediately. Otherwise, we wait for the camera to become available (or timeout)
- * before launching the secure camera.
- */
- public void startSecureCameraLaunch() {
- if (DEBUG) Log.d(TAG, "startSecureCameraLunch");
- if (areAllCamerasAvailable() || targetWillWaitForCameraAvailable()) {
- mKeyguardBottomArea.launchCamera();
- } else {
- mWaitingToLaunchSecureCamera = true;
- mHandler.postDelayed(mLaunchCameraRunnable, CAMERA_AVAILABILITY_TIMEOUT_MS);
- }
- }
-
- /**
- * Returns true if all of the cameras we are tracking are currently available.
- */
- private boolean areAllCamerasAvailable() {
- for (boolean cameraAvailable: mCameraAvailabilityMap.values()) {
- if (!cameraAvailable) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Determines if the secure camera app will wait for the camera hardware to become available
- * before trying to open the camera. If so, we can fire off an intent to start the secure
- * camera app before the camera is available. Otherwise, it is our responsibility to wait for
- * the camera hardware to become available before firing off the intent to start the secure
- * camera.
- *
- * Ideally we are able to fire off the secure camera intent as early as possibly so that, if the
- * camera is closing, it can continue to close while the secure camera app is opening. This
- * improves secure camera startup time.
- */
- private boolean targetWillWaitForCameraAvailable() {
- // Create intent that would launch the secure camera.
- Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
- .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- PackageManager packageManager = mContext.getPackageManager();
-
- // Get the list of applications that can handle the intent.
- final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
- intent, PackageManager.MATCH_DEFAULT_ONLY, KeyguardUpdateMonitor.getCurrentUser());
- if (appList.size() == 0) {
- if (DEBUG) Log.d(TAG, "No targets found for secure camera intent");
- return false;
- }
-
- // Get the application that the intent resolves to.
- ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
- KeyguardUpdateMonitor.getCurrentUser());
-
- if (resolved == null || resolved.activityInfo == null) {
- return false;
- }
-
- // If we would need to launch the resolver activity, then we can't assume that the target
- // is one that would wait for the camera.
- if (wouldLaunchResolverActivity(resolved, appList)) {
- if (DEBUG) Log.d(TAG, "Secure camera intent would launch resolver");
- return false;
- }
-
- // If the target doesn't have meta-data we must assume it won't wait for the camera.
- if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
- if (DEBUG) Log.d(TAG, "No meta-data found for secure camera application");
- return false;
- }
-
- // Check the secure camera app meta-data to see if it indicates that it will wait for the
- // camera to become available.
- boolean willWaitForCameraAvailability =
- resolved.activityInfo.metaData.getBoolean(META_DATA_WILL_WAIT_FOR_CAMERA_AVAILABLE);
-
- if (DEBUG) Log.d(TAG, "Target will wait for camera: " + willWaitForCameraAvailability);
-
- return willWaitForCameraAvailability;
- }
-
- /**
- * Determines if the activity that would be launched by the intent is the ResolverActivity.
- */
- private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
- // If the list contains the resolved activity, then it can't be the ResolverActivity itself.
- for (int i = 0; i < appList.size(); i++) {
- ResolveInfo tmp = appList.get(i);
- if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
- && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
- return false;
- }
- }
- return true;
- }
-}
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..394ff3f 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();
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..51c2208 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -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/tuner/QSPagingSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
new file mode 100644
index 0000000..4387b33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
@@ -0,0 +1,26 @@
+package com.android.systemui.tuner;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.util.AttributeSet;
+
+import com.android.systemui.statusbar.phone.QSTileHost;
+
+public class QSPagingSwitch extends TunerSwitch {
+
+ public static final String QS_PAGE_TILES =
+ "dwifi,dbt,inversion,dnd,cell,airplane,rotation,flashlight,location,"
+ + "hotspot,qwifi,qbt,qrotation,qflashlight,qairplane,cast";
+
+ public QSPagingSwitch(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean persistBoolean(boolean value) {
+ Settings.Secure.putString(getContext().getContentResolver(), QSTileHost.TILES_SETTING,
+ value ? QS_PAGE_TILES : "default");
+ return super.persistBoolean(value);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
index 37ac098..772f866 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
@@ -389,10 +389,11 @@
mView = super.createTileView(context);
return mView;
}
-
+
@Override
- public boolean supportsDualTargets() {
- return "wifi".equals(mSpec) || "bt".equals(mSpec);
+ public int getTileType() {
+ return "wifi".equals(mSpec) || "bt".equals(mSpec) ? QSTileView.QS_TYPE_DUAL
+ : QSTileView.QS_TYPE_NORMAL;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 96ad756..920f875 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -36,6 +36,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.tuner.TunerService.Tunable;
import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING;
@@ -55,6 +57,8 @@
private SwitchPreference mBatteryPct;
+ private Preference mQsTuner;
+
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -62,7 +66,8 @@
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
setHasOptionsMenu(true);
- findPreference(KEY_QS_TUNER).setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ mQsTuner = findPreference(KEY_QS_TUNER);
+ mQsTuner.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
@@ -96,6 +101,13 @@
}
}).show();
}
+ TunerService.get(getContext()).addTunable(mQsPaging, QSPanel.QS_PAGED_PANEL);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ TunerService.get(getContext()).removeTunable(mQsPaging);
}
@Override
@@ -167,4 +179,12 @@
return true;
}
};
+
+ private final Tunable mQsPaging = new Tunable() {
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ // Only enable QS rearranging when paging is off, because its very broken.
+ mQsTuner.setEnabled(newValue == null || Integer.parseInt(newValue) == 0);
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java
index 0740e08..54078b0 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java
@@ -1,16 +1,23 @@
package com.android.systemui.tuner;
import android.content.Context;
+import android.content.res.TypedArray;
import android.preference.SwitchPreference;
import android.provider.Settings;
import android.util.AttributeSet;
+import com.android.systemui.R;
import com.android.systemui.tuner.TunerService.Tunable;
public class TunerSwitch extends SwitchPreference implements Tunable {
+ private final boolean mDefault;
+
public TunerSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TunerSwitch);
+ mDefault = a.getBoolean(R.styleable.TunerSwitch_defValue, false);
}
@Override
@@ -27,7 +34,7 @@
@Override
public void onTuningChanged(String key, String newValue) {
- setChecked(newValue != null && Integer.parseInt(newValue) != 0);
+ setChecked(newValue != null ? Integer.parseInt(newValue) != 0 : mDefault);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 95a8d39..b2f527e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -104,6 +104,7 @@
private final SpTexts mSpTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
private final KeyguardManager mKeyguard;
+ private final AudioManager mAudioManager;
private final int mExpandButtonAnimationDuration;
private final ZenFooter mZenFooter;
private final LayoutTransition mLayoutTransition;
@@ -136,6 +137,7 @@
mCallback = callback;
mSpTexts = new SpTexts(mContext);
mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mDialog = new CustomDialog(mContext);
@@ -421,8 +423,9 @@
}
}
} else {
- final boolean vmute = row.ss.level == 0;
- mController.setStreamVolume(stream, vmute ? row.lastAudibleLevel : 0);
+ final boolean vmute = row.ss.level == row.ss.levelMin;
+ mController.setStreamVolume(stream,
+ vmute ? row.lastAudibleLevel : row.ss.levelMin);
}
row.userAttempt = 0; // reset the grace period, slider should update immediately
}
@@ -648,7 +651,8 @@
private void updateFooterH() {
if (D.BUG) Log.d(TAG, "updateFooterH");
final boolean wasVisible = mZenFooter.getVisibility() == View.VISIBLE;
- final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF;
+ final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF
+ && mAudioManager.isStreamAffectedByRingerMode(mActiveStream);
if (wasVisible != visible && !visible) {
prepareForCollapse();
}
@@ -1024,6 +1028,7 @@
final int minProgress = mRow.ss.levelMin * 100;
if (progress < minProgress) {
seekBar.setProgress(minProgress);
+ progress = minProgress;
}
}
final int userLevel = getImpliedLevel(seekBar, progress);
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..0ae0cf6 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
@@ -412,4 +412,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/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 57769e7..91c3d48 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -205,9 +205,7 @@
private final UserManager mUserManager;
- private final LockPatternUtils mLockPatternUtils;
-
- private int mCurrentUserId = UserHandle.USER_OWNER;
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
//TODO: Remove this hack
private boolean mInitialized;
@@ -230,7 +228,6 @@
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mSecurityPolicy = new SecurityPolicy();
mMainHandler = new MainHandler(mContext.getMainLooper());
- mLockPatternUtils = new LockPatternUtils(context);
registerBroadcastReceivers();
new AccessibilityContentObserver(mMainHandler).register(
context.getContentResolver());
@@ -653,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);
@@ -703,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();
@@ -866,17 +865,18 @@
}
// Called only during settings restore; currently supports only the owner user
+ // TODO: http://b/22388012
void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting) {
readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false);
readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true);
- UserState userState = getUserStateLocked(UserHandle.USER_OWNER);
+ UserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
userState.mEnabledServices.clear();
userState.mEnabledServices.addAll(mTempComponentNameSet);
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userState.mEnabledServices,
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
onUserStateChangedLocked(userState);
}
@@ -1283,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;
@@ -1487,6 +1490,7 @@
somthingChanged |= readHighTextContrastEnabledSettingLocked(userState);
somthingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
somthingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
+ somthingChanged |= readAutoclickEnabledSettingLocked(userState);
somthingChanged |= readDisplayColorAdjustmentSettingsLocked(userState);
return somthingChanged;
}
@@ -1525,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,
@@ -1646,10 +1662,6 @@
DisplayAdjustmentUtils.applyAdjustments(mContext, userState.mUserId);
}
- private boolean hasRunningServicesLocked(UserState userState) {
- return !userState.mBoundServices.isEmpty() || !userState.mBindingServices.isEmpty();
- }
-
private MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
IBinder windowToken = mGlobalWindowTokens.get(windowId);
if (windowToken == null) {
@@ -1677,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);
@@ -3783,6 +3796,7 @@
public boolean mIsTextHighContrastEnabled;
public boolean mIsEnhancedWebAccessibilityEnabled;
public boolean mIsDisplayMagnificationEnabled;
+ public boolean mIsAutoclickEnabled;
public boolean mIsFilterKeyEventsEnabled;
public boolean mHasDisplayColorAdjustment;
public boolean mAccessibilityFocusOnlyInActiveWindow;
@@ -3847,6 +3861,7 @@
mIsTouchExplorationEnabled = false;
mIsEnhancedWebAccessibilityEnabled = false;
mIsDisplayMagnificationEnabled = false;
+ mIsAutoclickEnabled = false;
}
public void destroyUiAutomationService() {
@@ -3871,6 +3886,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);
@@ -3903,6 +3921,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(
@@ -3944,6 +3964,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..2e6136f
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -0,0 +1,370 @@
+/*
+ * 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.content.Context;
+import android.os.Handler;
+import android.os.SystemClock;
+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 {
+ private static final String LOG_TAG = AutoclickController.class.getSimpleName();
+
+ // TODO: Control click delay via settings.
+ private static final int CLICK_DELAY_MS = 600;
+
+ private EventStreamTransformation mNext;
+ private Context mContext;
+
+ // Lazily created on the first mouse motion event.
+ private ClickScheduler mClickScheduler;
+
+ 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, CLICK_DELAY_MS);
+ }
+
+ 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 (mClickScheduler != null) {
+ mClickScheduler.cancel();
+ }
+ }
+
+ 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();
+ }
+ }
+
+ /**
+ * 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 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..85730cd 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -29,6 +29,8 @@
import android.os.Handler;
import android.os.SystemClock;
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;
@@ -253,7 +255,22 @@
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 +279,6 @@
}
}
- public void onDestroy() {
- // TODO: Implement
- }
-
private void clear(MotionEvent event, int policyFlags) {
switch (mCurrentState) {
case STATE_TOUCH_EXPLORING: {
@@ -304,9 +317,6 @@
mLongPressingPointerDeltaX = 0;
mLongPressingPointerDeltaY = 0;
mCurrentState = STATE_TOUCH_EXPLORING;
- if (mNext != null) {
- mNext.clear();
- }
mTouchExplorationInProgress = false;
mAms.onTouchInteractionEnd();
}
@@ -318,6 +328,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 +361,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();
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index fa87270..2385060 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);
@@ -2729,7 +2730,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 +2751,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();
}
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/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 4288fa2..882899e 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -177,7 +177,7 @@
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
- private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = 60*1000;
+ private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
index 6a67316..a0b5c15 100644
--- a/services/core/java/com/android/server/AnyMotionDetector.java
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -58,9 +58,6 @@
/** Current measurement state. */
private int mState;
- /** Threshold angle in degrees beyond which the device is considered moving. */
- private final float THRESHOLD_ANGLE = 2f;
-
/** Threshold energy above which the device is considered moving. */
private final float THRESHOLD_ENERGY = 5f;
@@ -88,6 +85,9 @@
private SensorManager mSensorManager;
private PowerManager.WakeLock mWakeLock;
+ /** Threshold angle in degrees beyond which the device is considered moving. */
+ private final float mThresholdAngle;
+
/** The minimum number of samples required to detect AnyMotion. */
private int mNumSufficientSamples;
@@ -106,7 +106,7 @@
private DeviceIdleCallback mCallback = null;
public AnyMotionDetector(PowerManager pm, Handler handler, SensorManager sm,
- DeviceIdleCallback callback) {
+ DeviceIdleCallback callback, float thresholdAngle) {
if (DEBUG) Slog.d(TAG, "AnyMotionDetector instantiated.");
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(false);
@@ -116,6 +116,7 @@
mMeasurementInProgress = false;
mState = STATE_INACTIVE;
mCallback = callback;
+ mThresholdAngle = thresholdAngle;
mRunningStats = new RunningSignalStats();
mNumSufficientSamples = (int) Math.ceil(
((double)ORIENTATION_MEASUREMENT_DURATION_MILLIS / SAMPLING_INTERVAL_MILLIS));
@@ -224,8 +225,9 @@
Vector3 previousGravityVectorNormalized = mPreviousGravityVector.normalized();
Vector3 currentGravityVectorNormalized = mCurrentGravityVector.normalized();
float angle = previousGravityVectorNormalized.angleBetween(currentGravityVectorNormalized);
- if (DEBUG) Slog.d(TAG, "getStationaryStatus: angle = " + angle);
- if ((angle < THRESHOLD_ANGLE) && (mRunningStats.getEnergy() < THRESHOLD_ENERGY)) {
+ if (DEBUG) Slog.d(TAG, "getStationaryStatus: angle = " + angle
+ + " energy = " + mRunningStats.getEnergy());
+ if ((angle < mThresholdAngle) && (mRunningStats.getEnergy() < THRESHOLD_ENERGY)) {
return RESULT_STATIONARY;
} else if (Float.isNaN(angle)) {
/**
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 50bd544..fd67b41 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -271,9 +271,10 @@
int sysUiUid = -1;
try {
sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui",
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
} catch (PackageManager.NameNotFoundException e) {
- Log.wtf(TAG, "Unable to resolve SystemUI's UID.", e);
+ // Some platforms, such as wearables do not have a system ui.
+ Log.w(TAG, "Unable to resolve SystemUI's UID.", e);
}
mSystemUiUid = sysUiUid;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e19447d..6190a5a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -359,6 +359,9 @@
*/
private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31;
+ /** Handler thread used for both of the handlers below. */
+ @VisibleForTesting
+ protected final HandlerThread mHandlerThread;
/** Handler used for internal events. */
final private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -511,7 +514,6 @@
ArrayList<NetworkAgentInfo> list = mTypeLists[type];
if (list.contains(nai)) {
- loge("Attempting to register duplicate agent for type " + type + ": " + nai);
return;
}
@@ -615,6 +617,11 @@
}
private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
+ @VisibleForTesting
+ protected HandlerThread createHandlerThread() {
+ return new HandlerThread("ConnectivityServiceThread");
+ }
+
public ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
if (DBG) log("ConnectivityService starting up");
@@ -628,10 +635,10 @@
mDefaultMobileDataRequest = createInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_CELLULAR);
- HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
- handlerThread.start();
- mHandler = new InternalHandler(handlerThread.getLooper());
- mTrackerHandler = new NetworkStateTrackerHandler(handlerThread.getLooper());
+ mHandlerThread = createHandlerThread();
+ mHandlerThread.start();
+ mHandler = new InternalHandler(mHandlerThread.getLooper());
+ mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
// setup our unique device name
if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
@@ -1459,7 +1466,7 @@
}
private void enforceKeepalivePermission() {
- mContext.enforceCallingPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
+ mContext.enforceCallingOrSelfPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
}
public void sendConnectedBroadcast(NetworkInfo info) {
@@ -1524,10 +1531,14 @@
final long ident = Binder.clearCallingIdentity();
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+ final NetworkInfo ni = intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {
+ intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ }
final IBatteryStats bs = BatteryStatsService.getService();
try {
- NetworkInfo ni = intent.getParcelableExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
bs.noteConnectivityChanged(intent.getIntExtra(
ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE),
ni != null ? ni.getState().toString() : "?");
@@ -2259,8 +2270,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);
}
}
}
@@ -2377,8 +2389,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);
}
}
}
@@ -3628,9 +3641,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);
@@ -4276,7 +4304,7 @@
boolean keep = newNetwork.isVPN();
boolean isNewDefault = false;
NetworkAgentInfo oldDefaultNetwork = null;
- if (DBG) log("rematching " + newNetwork.name());
+ if (VDBG) log("rematching " + newNetwork.name());
// Find and migrate to this Network any NetworkRequests for
// which this network is now the best.
ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
@@ -4313,6 +4341,7 @@
}
if (currentNetwork == null ||
currentNetwork.getCurrentScore() < newNetwork.getCurrentScore()) {
+ if (DBG) log("rematch for " + newNetwork.name());
if (currentNetwork != null) {
if (DBG) log(" accepting network in place of " + currentNetwork.name());
currentNetwork.networkRequests.remove(nri.request.requestId);
@@ -4612,7 +4641,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 80fd441..2f4ad3f 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -128,7 +128,8 @@
private boolean mNotMoving;
private boolean mLocating;
private boolean mLocated;
- private boolean mHaveGps;
+ private boolean mHasGps;
+ private boolean mHasNetworkLocation;
private Location mLastGenericLocation;
private Location mLastGpsLocation;
@@ -882,17 +883,37 @@
mDisplayManager = (DisplayManager) getContext().getSystemService(
Context.DISPLAY_SERVICE);
mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
- mSigMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
- mLocationManager = (LocationManager) getContext().getSystemService(
- Context.LOCATION_SERVICE);
- mLocationRequest = new LocationRequest()
- .setQuality(LocationRequest.ACCURACY_FINE)
- .setInterval(0)
- .setFastestInterval(0)
- .setNumUpdates(1);
+ int sigMotionSensorId = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
+ if (sigMotionSensorId > 0) {
+ mSigMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
+ }
+ if (mSigMotionSensor == null && getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
+ mSigMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_WRIST_TILT_GESTURE);
+ }
+ if (mSigMotionSensor == null) {
+ // As a last ditch, fall back to SMD.
+ mSigMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_SIGNIFICANT_MOTION);
+ }
+ if (getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
+ mLocationManager = (LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE);
+ mLocationRequest = new LocationRequest()
+ .setQuality(LocationRequest.ACCURACY_FINE)
+ .setInterval(0)
+ .setFastestInterval(0)
+ .setNumUpdates(1);
+ }
+
+ float angleThreshold = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;
mAnyMotionDetector = new AnyMotionDetector(
(PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
- mHandler, mSensorManager, this);
+ mHandler, mSensorManager, this, angleThreshold);
Intent intent = new Intent(ACTION_STEP_IDLE_STATE)
.setPackage("android")
@@ -1279,17 +1300,30 @@
EventLogTags.writeDeviceIdle(mState, "step");
cancelSensingAlarmLocked();
scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT);
- mLocating = true;
- mLocationManager.requestLocationUpdates(mLocationRequest, mGenericLocationListener,
- mHandler.getLooper());
- if (mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
- mHaveGps = true;
+ if (mLocationManager != null
+ && mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
+ mLocationManager.requestLocationUpdates(mLocationRequest,
+ mGenericLocationListener, mHandler.getLooper());
+ mLocating = true;
+ } else {
+ mHasNetworkLocation = false;
+ }
+ if (mLocationManager != null
+ && mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
+ mHasGps = true;
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
mGpsLocationListener, mHandler.getLooper());
+ mLocating = true;
} else {
- mHaveGps = false;
+ mHasGps = false;
}
- break;
+ // If we have a location provider, we're all set, the listeners will move state
+ // forward.
+ if (mLocating) {
+ break;
+ }
+
+ // Otherwise, we have to move from locating into idle maintenance.
case STATE_LOCATING:
cancelSensingAlarmLocked();
cancelLocatingLocked();
@@ -1346,7 +1380,7 @@
}
if (DEBUG) Slog.d(TAG, "Generic location: " + location);
mLastGenericLocation = new Location(location);
- if (location.getAccuracy() > mConstants.LOCATION_ACCURACY && mHaveGps) {
+ if (location.getAccuracy() > mConstants.LOCATION_ACCURACY && mHasGps) {
return;
}
mLocated = true;
@@ -1413,9 +1447,9 @@
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
if (mSigMotionSensor == null) {
- // If there is no significant motion sensor on this device, then we won't schedule
+ // 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 exeuction of device idling, although it is still possible to
+ // turns off normal execution of device idling, although it is still possible to
// manually poke it by pretending like the alarm is going off.
return;
}
@@ -1655,7 +1689,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)) {
@@ -1902,8 +1936,9 @@
pw.print(" mSigMotionActive="); pw.println(mSigMotionActive);
pw.print(" mSensing="); pw.print(mSensing); pw.print(" mNotMoving=");
pw.println(mNotMoving);
- pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHaveGps=");
- pw.print(mHaveGps); pw.print(" mLocated="); pw.println(mLocated);
+ pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
+ pw.print(mHasGps); pw.print(" mHasNetwork=");
+ pw.print(mHasNetworkLocation); pw.print(" mLocated="); pw.println(mLocated);
if (mLastGenericLocation != null) {
pw.print(" mLastGenericLocation="); pw.println(mLastGenericLocation);
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 342a3ef..bd7d4b2 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -17,7 +17,6 @@
package com.android.server;
import android.app.ActivityManager;
-import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -33,10 +32,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 +46,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 +97,20 @@
*/
private int mCameraLaunchLastEventExtra = 0;
+ /**
+ * Whether camera double tap power button gesture is currently enabled;
+ */
+ private boolean mCameraDoubleTapPowerEnabled;
+ private long mLastPowerDownWhileNonInteractive;
+ private long mLastPowerDownWhileInteractive;
+
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,71 @@
!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;
+ synchronized (this) {
+ if (!mCameraDoubleTapPowerEnabled) {
+ mLastPowerDownWhileNonInteractive = 0;
+ mLastPowerDownWhileInteractive = 0;
+ return false;
+ }
+ if (event.getEventTime() - mLastPowerDownWhileNonInteractive
+ < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
+ launched = true;
+ intercept = true;
+ } else if (event.getEventTime() - mLastPowerDownWhileInteractive
+ < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
+ launched = true;
+ }
+ mLastPowerDownWhileNonInteractive = interactive ? 0 : event.getEventTime();
+ mLastPowerDownWhileInteractive = interactive ? event.getEventTime() : 0;
+ }
+ if (launched) {
+ Slog.i(TAG, "Power button double tap gesture detected, launching camera.");
+ launched = handleCameraLaunchGesture(false /* useWakelock */,
+ MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE);
+ }
+ return intercept && launched;
+ }
+
+ /**
+ * @return true if camera was launched, false otherwise.
+ */
+ private boolean handleCameraLaunchGesture(boolean useWakelock, int logCategory) {
+ 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();
+ MetricsLogger.action(mContext, logCategory);
+ return true;
}
private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
@@ -222,8 +310,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 +321,7 @@
public void onChange(boolean selfChange, android.net.Uri uri, int userId) {
if (userId == mUserId) {
updateCameraRegistered();
+ updateCameraDoubleTapPowerEnabled();
}
}
};
@@ -244,38 +334,19 @@
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 */,
+ 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/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..540f8cb 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;
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 94316fe..e8b90d8 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -25,6 +25,7 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.service.persistentdata.IPersistentDataBlockService;
import android.util.Slog;
@@ -84,7 +85,7 @@
mContext = context;
mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
mBlockDeviceSize = -1; // Load lazily
- mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
+ mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
}
private int getAllowedUid(int userHandle) {
@@ -131,9 +132,12 @@
}
}
- private void enforceIsOwner() {
- if (!Binder.getCallingUserHandle().isOwner()) {
- throw new SecurityException("Only the Owner is allowed to change OEM unlock state");
+ private void enforceIsAdmin() {
+ final int userId = UserHandle.getCallingUserId();
+ final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId);
+ if (!isAdmin) {
+ throw new SecurityException(
+ "Only the Admin user is allowed to change OEM unlock state");
}
}
private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
@@ -434,7 +438,7 @@
return;
}
enforceOemUnlockPermission();
- enforceIsOwner();
+ enforceIsAdmin();
synchronized (mLock) {
doSetOemUnlockEnabledLocked(enabled);
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index fda6479..92e6814 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.content.Context;
+import android.os.Trace;
import android.util.Slog;
import java.lang.reflect.Constructor;
@@ -75,43 +76,48 @@
*/
@SuppressWarnings("unchecked")
public <T extends SystemService> T startService(Class<T> serviceClass) {
- final String name = serviceClass.getName();
- Slog.i(TAG, "Starting " + name);
-
- // Create the service.
- if (!SystemService.class.isAssignableFrom(serviceClass)) {
- throw new RuntimeException("Failed to create " + name
- + ": service must extend " + SystemService.class.getName());
- }
- final T service;
try {
- Constructor<T> constructor = serviceClass.getConstructor(Context.class);
- service = constructor.newInstance(mContext);
- } catch (InstantiationException ex) {
- throw new RuntimeException("Failed to create service " + name
- + ": service could not be instantiated", ex);
- } catch (IllegalAccessException ex) {
- throw new RuntimeException("Failed to create service " + name
- + ": service must have a public constructor with a Context argument", ex);
- } catch (NoSuchMethodException ex) {
- throw new RuntimeException("Failed to create service " + name
- + ": service must have a public constructor with a Context argument", ex);
- } catch (InvocationTargetException ex) {
- throw new RuntimeException("Failed to create service " + name
- + ": service constructor threw an exception", ex);
- }
+ final String name = serviceClass.getName();
+ Slog.i(TAG, "Starting " + name);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);
- // Register it.
- mServices.add(service);
+ // Create the service.
+ if (!SystemService.class.isAssignableFrom(serviceClass)) {
+ throw new RuntimeException("Failed to create " + name
+ + ": service must extend " + SystemService.class.getName());
+ }
+ final T service;
+ try {
+ Constructor<T> constructor = serviceClass.getConstructor(Context.class);
+ service = constructor.newInstance(mContext);
+ } catch (InstantiationException ex) {
+ throw new RuntimeException("Failed to create service " + name
+ + ": service could not be instantiated", ex);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException("Failed to create service " + name
+ + ": service must have a public constructor with a Context argument", ex);
+ } catch (NoSuchMethodException ex) {
+ throw new RuntimeException("Failed to create service " + name
+ + ": service must have a public constructor with a Context argument", ex);
+ } catch (InvocationTargetException ex) {
+ throw new RuntimeException("Failed to create service " + name
+ + ": service constructor threw an exception", ex);
+ }
- // Start it.
- try {
- service.onStart();
- } catch (RuntimeException ex) {
- throw new RuntimeException("Failed to start service " + name
- + ": onStart threw an exception", ex);
+ // Register it.
+ mServices.add(service);
+
+ // Start it.
+ try {
+ service.onStart();
+ } catch (RuntimeException ex) {
+ throw new RuntimeException("Failed to start service " + name
+ + ": onStart threw an exception", ex);
+ }
+ return service;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
- return service;
}
/**
@@ -127,18 +133,22 @@
mCurrentPhase = phase;
Slog.i(TAG, "Starting phase " + mCurrentPhase);
-
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- try {
- service.onBootPhase(mCurrentPhase);
- } catch (Exception ex) {
- throw new RuntimeException("Failed to boot service "
- + service.getClass().getName()
- + ": onBootPhase threw an exception during phase "
- + mCurrentPhase, ex);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "OnBootPhase " + phase);
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onBootPhase(mCurrentPhase);
+ } catch (Exception ex) {
+ throw new RuntimeException("Failed to boot service "
+ + service.getClass().getName()
+ + ": onBootPhase threw an exception during phase "
+ + mCurrentPhase, ex);
+ }
}
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
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/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..dbf1288 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -32,6 +32,7 @@
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 +123,7 @@
private final Context mContext;
private final PackageManager mPackageManager;
+ private final AppOpsManager mAppOpsManager;
private UserManager mUserManager;
private final MessageHandler mMessageHandler;
@@ -266,6 +268,7 @@
IAccountAuthenticatorCache authenticatorCache) {
mContext = context;
mPackageManager = packageManager;
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mMessageHandler = new MessageHandler(FgThread.get().getLooper());
@@ -510,7 +513,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 +879,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 +1000,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 +1013,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 +2526,10 @@
* Returns the accounts visible to the client within the context of a specific user
* @hide
*/
- public Account[] getAccounts(int userId) {
+ 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];
}
@@ -2585,15 +2591,16 @@
}
@Override
- public Account[] getAccountsAsUser(String type, int userId) {
- return getAccountsAsUser(type, userId, null, -1);
+ public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
+ return getAccountsAsUser(type, userId, null, -1, opPackageName);
}
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 +2621,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];
@@ -2755,22 +2764,24 @@
}
@Override
- public Account[] getAccounts(String type) {
- return getAccountsAsUser(type, UserHandle.getCallingUserId());
+ public Account[] getAccounts(String type, String opPackageName) {
+ return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
}
@Override
- public Account[] getAccountsForPackage(String packageName, int uid) {
+ 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) {
+ public Account[] getAccountsByTypeForPackage(String type, String packageName,
+ String opPackageName) {
int packageUid = -1;
try {
packageUid = AppGlobals.getPackageManager().getPackageUid(
@@ -2779,14 +2790,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 +2812,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 +3713,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 +3782,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 +3800,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);
@@ -3891,8 +3899,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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0119000..4949138 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2085,7 +2085,8 @@
}
private boolean collectPackageServicesLocked(String packageName, Set<String> filterByClasses,
- boolean evenPersistent, boolean doit, ArrayMap<ComponentName, ServiceRecord> services) {
+ boolean evenPersistent, boolean doit, boolean killProcess,
+ ArrayMap<ComponentName, ServiceRecord> services) {
boolean didSomething = false;
for (int i = services.size() - 1; i >= 0; i--) {
ServiceRecord service = services.valueAt(i);
@@ -2101,7 +2102,7 @@
didSomething = true;
Slog.i(TAG, " Force stopping service " + service);
if (service.app != null) {
- service.app.removed = true;
+ service.app.removed = killProcess;
if (!service.app.persistent) {
service.app.services.remove(service);
}
@@ -2118,7 +2119,7 @@
}
boolean bringDownDisabledPackageServicesLocked(String packageName, Set<String> filterByClasses,
- int userId, boolean evenPersistent, boolean doit) {
+ int userId, boolean evenPersistent, boolean killProcess, boolean doit) {
boolean didSomething = false;
if (mTmpCollectionResults != null) {
@@ -2128,7 +2129,7 @@
if (userId == UserHandle.USER_ALL) {
for (int i = mServiceMap.size() - 1; i >= 0; i--) {
didSomething |= collectPackageServicesLocked(packageName, filterByClasses,
- evenPersistent, doit, mServiceMap.valueAt(i).mServicesByName);
+ evenPersistent, doit, killProcess, mServiceMap.valueAt(i).mServicesByName);
if (!doit && didSomething) {
return true;
}
@@ -2138,7 +2139,7 @@
if (smap != null) {
ArrayMap<ComponentName, ServiceRecord> items = smap.mServicesByName;
didSomething = collectPackageServicesLocked(packageName, filterByClasses,
- evenPersistent, doit, items);
+ evenPersistent, doit, killProcess, items);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4aef23b..b21e9027 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;
@@ -406,6 +405,17 @@
private static final int PERSISTENT_MASK =
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
+
+ // Delay to disable app launch boost
+ static final int APP_BOOST_MESSAGE_DELAY = 3000;
+ // Lower delay than APP_BOOST_MESSAGE_DELAY to disable the boost
+ static final int APP_BOOST_TIMEOUT = 2500;
+
+ private static native int nativeMigrateToBoost();
+ private static native int nativeMigrateFromBoost();
+ private boolean mIsBoosted = false;
+ private long mBoostStartTime = 0;
+
/** All system services */
SystemServiceManager mSystemServiceManager;
@@ -1367,6 +1377,7 @@
static final int REPORT_TIME_TRACKER_MSG = 55;
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 FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1885,7 +1896,9 @@
}
case FINISH_BOOTING_MSG: {
if (msg.arg1 != 0) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
finishBooting();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
if (msg.arg2 != 0) {
enableScreenAfterBoot();
@@ -2045,6 +2058,20 @@
// it is finished we make sure it is reset to its default.
mUserIsMonkey = false;
} break;
+ case APP_BOOST_DEACTIVATE_MSG : {
+ synchronized(ActivityManagerService.this) {
+ if (mIsBoosted) {
+ if (mBoostStartTime < (SystemClock.uptimeMillis() - APP_BOOST_TIMEOUT)) {
+ nativeMigrateFromBoost();
+ mIsBoosted = false;
+ mBoostStartTime = 0;
+ } else {
+ Message newmsg = mHandler.obtainMessage(APP_BOOST_DEACTIVATE_MSG);
+ mHandler.sendMessageDelayed(newmsg, APP_BOOST_TIMEOUT);
+ }
+ }
+ }
+ } break;
}
}
};
@@ -2701,31 +2728,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);
}
}
@@ -2735,21 +2748,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 {
@@ -3177,6 +3175,16 @@
app = null;
}
+ // app launch boost for big.little configurations
+ // use cpusets to migrate freshly launched tasks to big cores
+ synchronized(ActivityManagerService.this) {
+ nativeMigrateToBoost();
+ mIsBoosted = true;
+ mBoostStartTime = SystemClock.uptimeMillis();
+ Message msg = mHandler.obtainMessage(APP_BOOST_DEACTIVATE_MSG);
+ mHandler.sendMessageDelayed(msg, APP_BOOST_MESSAGE_DELAY);
+ }
+
// We don't have to do anything more if:
// (1) There is an existing application record; and
// (2) The caller doesn't think it is dead, OR there is no thread
@@ -4186,7 +4194,7 @@
throw new IllegalArgumentException("Task " + taskId + " not found.");
}
if (task.getRootActivity() != null) {
- moveTaskToFrontLocked(task.taskId, 0, null);
+ moveTaskToFrontLocked(task.taskId, 0, options);
return ActivityManager.START_TASK_TO_FRONT;
}
callingUid = task.mCallingUid;
@@ -5608,7 +5616,7 @@
}
private void cleanupDisabledPackageComponentsLocked(
- String packageName, int userId, String[] changedClasses) {
+ String packageName, int userId, boolean killProcess, String[] changedClasses) {
Set<String> disabledClasses = null;
boolean packageDisabled = false;
@@ -5678,7 +5686,7 @@
// Clean-up disabled services.
mServices.bringDownDisabledPackageServicesLocked(
- packageName, disabledClasses, userId, false, true);
+ packageName, disabledClasses, userId, false, killProcess, true);
// Clean-up disabled providers.
ArrayList<ContentProviderRecord> providers = new ArrayList<>();
@@ -5763,7 +5771,7 @@
}
if (mServices.bringDownDisabledPackageServicesLocked(
- packageName, null, userId, evenPersistent, doit)) {
+ packageName, null, userId, evenPersistent, true, doit)) {
if (!doit) {
return true;
}
@@ -6450,7 +6458,9 @@
mBootAnimationComplete = true;
}
if (callFinishBooting) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
finishBooting();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -6465,7 +6475,9 @@
}
if (booting) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
finishBooting();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
if (enableScreen) {
@@ -8638,7 +8650,7 @@
}
@Override
- public void resizeTask(int taskId, Rect bounds) {
+ public void resizeTask(int taskId, Rect bounds, boolean resizedByUser) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"resizeTask()");
long ident = Binder.clearCallingIdentity();
@@ -8649,7 +8661,7 @@
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return;
}
- mStackSupervisor.resizeTaskLocked(task, bounds);
+ mStackSupervisor.resizeTaskLocked(task, bounds, resizedByUser);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -8678,58 +8690,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)) {
@@ -9091,6 +9051,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,
@@ -16849,7 +16838,9 @@
boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);
boolean fullUninstall = removed &&
!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
+ final boolean killProcess =
+ !intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);
+ if (killProcess) {
forceStopPackageLocked(ssp, UserHandle.getAppId(
intent.getIntExtra(Intent.EXTRA_UID, -1)),
false, true, true, false, fullUninstall, userId,
@@ -16869,7 +16860,7 @@
mBatteryStatsService.notePackageUninstalled(ssp);
}
} else {
- cleanupDisabledPackageComponentsLocked(ssp, userId,
+ cleanupDisabledPackageComponentsLocked(ssp, userId, killProcess,
intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST));
}
@@ -17411,8 +17402,10 @@
}
// Can't call out of the system process with a lock held, so post a message.
- mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
- app.instrumentationUiAutomationConnection).sendToTarget();
+ if (app.instrumentationUiAutomationConnection != null) {
+ mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
+ app.instrumentationUiAutomationConnection).sendToTarget();
+ }
app.instrumentationWatcher = null;
app.instrumentationUiAutomationConnection = null;
@@ -17667,7 +17660,7 @@
}
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);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index f50df3a..3fcffd7 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -17,9 +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.*;
@@ -31,7 +33,7 @@
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;
@@ -391,7 +393,9 @@
void setBounds(Rect bounds) {
mBounds = mFullscreen ? null : new Rect(bounds);
- mTaskPositioner.configure(bounds);
+ if (mTaskPositioner != null) {
+ mTaskPositioner.configure(bounds);
+ }
}
boolean okToShowLocked(ActivityRecord r) {
@@ -1277,28 +1281,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. */
@@ -1319,54 +1322,53 @@
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();
+ }
+
+ if (focusedStackId == DOCKED_STACK_ID
+ && stackIndex == (mStacks.indexOf(focusedStack) - 1)) {
+ // Stacks directly behind the docked stack are always visible.
+ return true;
+ }
+
+ if (mStackId == HOME_STACK_ID && focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ // Home stack is always visible behind the fullscreen stack with a translucent activity.
+ // This is done so that the home stack can act as a background to the translucent
+ // activity.
+ return hasTranslucentActivity(focusedStack);
+ }
+
+ 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;
}
}
@@ -1399,16 +1401,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) {
@@ -1428,7 +1428,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, false);
}
if (r.app == null || r.app.thread == null) {
@@ -1506,18 +1506,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.
@@ -1565,9 +1565,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 &&
@@ -3498,7 +3501,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);
@@ -3975,8 +3978,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);
@@ -4061,12 +4064,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 +4164,13 @@
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 boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume,
+ boolean preserveWindow) {
List<ResultInfo> results = null;
List<ReferrerIntent> newIntents = null;
if (andResume) {
@@ -4168,7 +4179,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 +4194,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.
@@ -4549,10 +4560,11 @@
boolean toTop) {
TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
voiceInteractor);
+ // add the task to stack first, mTaskPositioner might need the stack association
+ addTask(task, toTop, false);
if (mTaskPositioner != null) {
mTaskPositioner.updateDefaultBounds(task, mTaskHistory, info.initialLayout);
}
- addTask(task, toTop, false);
return task;
}
@@ -4589,25 +4601,20 @@
void addConfigOverride(ActivityRecord r, TaskRecord task) {
final Rect bounds = task.getLaunchBounds();
- final Configuration config =
- mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
- 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);
- if (config != null) {
- task.updateOverrideConfiguration(config, bounds);
- }
+ task.updateOverrideConfiguration(bounds);
+ mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
+ 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, !r.isHomeActivity());
r.taskConfigOverride = task.mOverrideConfig;
}
private void setAppTask(ActivityRecord r, TaskRecord task) {
final Rect bounds = task.getLaunchBounds();
- final Configuration config =
- mWindowManager.setAppTask(r.appToken, task.taskId, task.getLaunchBounds());
- if (config != null) {
- task.updateOverrideConfiguration(config, bounds);
- }
+ task.updateOverrideConfiguration(bounds);
+ mWindowManager.setAppTask(
+ r.appToken, task.taskId, task.getLaunchBounds(), task.mOverrideConfig);
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 c86056b..d3cea8d 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -97,7 +97,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
-import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -206,6 +205,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;
@@ -331,8 +337,12 @@
/** 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<>();
/**
* Description of a request to start a new activity, which has been held
@@ -1762,7 +1772,7 @@
return ACTIVITY_RESTRICTION_NONE;
}
- ActivityStack computeStackFocus(ActivityRecord r, boolean newTask) {
+ ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds) {
final TaskRecord task = r.task;
// On leanback only devices we should keep all activities in the same stack.
@@ -1813,10 +1823,10 @@
}
// If there is no suitable dynamic stack then we figure out which static stack to use.
- stack = getStack(
- task != null
- ? task.getLaunchStackId(mFocusedStack) : FULLSCREEN_WORKSPACE_STACK_ID,
- CREATE_IF_NEEDED, ON_TOP);
+ int stackId = task != null ? task.getLaunchStackId() :
+ bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
+ FULLSCREEN_WORKSPACE_STACK_ID;
+ stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
return stack;
@@ -1844,6 +1854,22 @@
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
+ boolean overrideBounds = false;
+ Rect newBounds = null;
+ if (r.info.resizeable || (inTask != null && inTask.mResizeable)) {
+ if (intent.hasExtra(ActivityOptions.KEY_BOUNDS)) {
+ overrideBounds = true;
+ newBounds = Rect.unflattenFromString(
+ intent.getStringExtra(ActivityOptions.KEY_BOUNDS));
+ } else if (options != null) {
+ ActivityOptions opts = new ActivityOptions(options);
+ if (opts.hasBounds()) {
+ overrideBounds = true;
+ newBounds = opts.getBounds();
+ }
+ }
+ }
+
// In some flows in to this function, we retrieve the task record and hold on to it
// without a lock before calling back in to here... so the task at this point may
// not actually be in recents. Check for that, and if it isn't in recents just
@@ -2197,7 +2223,8 @@
if (task != null && task.stack == null) {
// Target stack got cleared when we all activities were removed
// above. Go ahead and reset it.
- targetStack = computeStackFocus(sourceRecord, false /* newTask */);
+ targetStack = computeStackFocus(
+ sourceRecord, false /* newTask */, null /* bounds */);
targetStack.addTask(
task, !launchTaskBehind /* toTop */, false /* moving */);
}
@@ -2323,7 +2350,7 @@
if (r.resultTo == null && inTask == null && !addingToTask
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
- targetStack = computeStackFocus(r, newTask);
+ targetStack = computeStackFocus(r, newTask, newBounds);
if (doResume) {
targetStack.moveToFront("startingNewTask");
}
@@ -2334,6 +2361,9 @@
newTaskIntent != null ? newTaskIntent : intent,
voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
taskToAffiliate);
+ if (overrideBounds) {
+ r.task.updateOverrideConfiguration(newBounds);
+ }
if (DEBUG_TASKS) Slog.v(TAG_TASKS,
"Starting new activity " + r + " in new task " + r.task);
} else {
@@ -2418,6 +2448,14 @@
Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
+ if (overrideBounds) {
+ inTask.updateOverrideConfiguration(newBounds);
+ int stackId = inTask.getLaunchStackId();
+ if (stackId != inTask.stack.mStackId) {
+ moveTaskToStackUncheckedLocked(
+ inTask, stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront");
+ }
+ }
targetStack = inTask.stack;
targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, r.appTimeTracker,
"inTaskToFront");
@@ -2455,7 +2493,7 @@
// This not being started from an existing activity, and not part
// of a new task... just put it in the top task, though these days
// this case should never happen.
- targetStack = computeStackFocus(r, newTask);
+ targetStack = computeStackFocus(r, newTask, null /* bounds */);
if (doResume) {
targetStack.moveToFront("addingToTopTask");
}
@@ -2814,9 +2852,26 @@
+ task + " to front. Stack is null");
return;
}
- task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
+
+ int stackId = task.stack.mStackId;
+ if (task.mResizeable && options != null) {
+ ActivityOptions opts = new ActivityOptions(options);
+ if (opts.hasBounds()) {
+ Rect bounds = opts.getBounds();
+ task.updateOverrideConfiguration(bounds);
+ mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, false);
+ stackId = task.getLaunchStackId();
+ }
+ }
+
+ if (stackId != task.stack.mStackId) {
+ moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, FORCE_FOCUS, reason);
+ } else {
+ task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
task.getTopActivity() == null ? null : task.getTopActivity().appTimeTracker,
reason);
+ }
+
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
}
@@ -2917,19 +2972,29 @@
ActivityRecord r = stack.topRunningActivityLocked(null);
final boolean resizeTasks = r != null && r.task.mResizeable;
- final IntArray changedTaskIds = new IntArray(stack.numTasks());
- final List<Configuration> newTaskConfigs = new ArrayList<>(stack.numTasks());
- stack.mFullscreen = mWindowManager.resizeStack(
- stackId, bounds, resizeTasks, changedTaskIds, newTaskConfigs);
- for (int i = changedTaskIds.size() - 1; i >= 0; i--) {
- final TaskRecord task = anyTaskForIdLocked(changedTaskIds.get(i), false);
- if (task == null) {
- Slog.wtf(TAG, "Task in WindowManager, but not in ActivityManager???");
- continue;
- }
- task.updateOverrideConfiguration(newTaskConfigs.get(i), bounds);
- }
+ 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);
+ 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);
if (stack.mStackId == DOCKED_STACK_ID) {
// Dock stack funness...Yay!
if (stack.mFullscreen) {
@@ -2980,7 +3045,7 @@
stack.setBounds(bounds);
if (r != null) {
- final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
+ final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, false);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
ensureActivitiesVisibleLocked(r, 0);
@@ -2990,7 +3055,7 @@
}
}
- void resizeTaskLocked(TaskRecord task, Rect bounds) {
+ void resizeTaskLocked(TaskRecord task, Rect bounds, boolean resizedByUser) {
if (!task.mResizeable) {
Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
return;
@@ -3005,7 +3070,7 @@
// Task doesn't exist in window manager yet (e.g. was restored from recents).
// All we can do for now is update the bounds so it can be used when the task is
// added to window manager.
- task.mBounds = task.mLastNonFullscreenBounds = new Rect(bounds);
+ task.updateOverrideConfiguration(bounds);
if (task.stack != null && task.stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) {
// re-restore the task so it can have the proper stack association.
restoreRecentTaskLocked(task, FREEFORM_WORKSPACE_STACK_ID);
@@ -3023,26 +3088,40 @@
&& stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) {
stackId = FREEFORM_WORKSPACE_STACK_ID;
}
- if (stackId != task.stack.mStackId) {
- final String reason = "resizeTask";
- final ActivityStack stack =
- moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, reason);
+ final boolean changedStacks = stackId != task.stack.mStackId;
+ if (changedStacks) {
+ moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
}
- final Configuration overrideConfig = mWindowManager.resizeTask(task.taskId, bounds);
- if (task.updateOverrideConfiguration(overrideConfig, bounds)) {
+ final Configuration overrideConfig = task.updateOverrideConfiguration(bounds);
+ // This variable holds information whether the configuration didn't change in a signficant
+ // way and the activity was kept the way it was. If it's false, it means the activity had
+ // to be relaunched due to configuration change.
+ boolean kept = true;
+ if (overrideConfig != null) {
ActivityRecord r = task.topRunningActivityLocked(null);
if (r != null) {
final ActivityStack stack = task.stack;
- final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
+ final boolean preserveWindow = resizedByUser && !changedStacks;
+ kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
+ // All other activities must be made visible with their correct configuration.
ensureActivitiesVisibleLocked(r, 0);
- if (!updated) {
+ if (!kept) {
resumeTopActivitiesLocked(stack, null, null);
+ if (changedStacks && 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.
+ mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
+ }
}
}
}
+ mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, kept);
}
ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
@@ -3077,8 +3156,7 @@
*/
private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
if (stackId == INVALID_STACK_ID) {
- stackId = mLeanbackOnlyDevice ?
- mHomeStack.mStackId : task.getLaunchStackId(mFocusedStack);
+ stackId = mLeanbackOnlyDevice ? mHomeStack.mStackId : task.getLaunchStackId();
}
if (task.stack != null) {
// Task has already been restored once. See if we need to do anything more
@@ -3165,12 +3243,12 @@
// 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, false);
} else if (stackId == FREEFORM_WORKSPACE_STACK_ID
&& task.mBounds == null && task.mLastNonFullscreenBounds != null) {
- resizeTaskLocked(task, task.mLastNonFullscreenBounds);
+ resizeTaskLocked(task, task.mLastNonFullscreenBounds, false);
} else if (stackId == DOCKED_STACK_ID) {
- resizeTaskLocked(task, stack.mBounds);
+ resizeTaskLocked(task, stack.mBounds, false);
}
// The task might have already been running and its visibility needs to be synchronized with
@@ -4782,4 +4860,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/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a956c56..960cbf1 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -509,7 +509,7 @@
break;
}
int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != r.appOp
+ if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
&& mService.mAppOpsService.noteOperation(appOp,
filter.receiverList.uid, filter.packageName)
!= AppOpsManager.MODE_ALLOWED) {
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 814e8b4..424ceb1 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -344,7 +344,7 @@
}
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);
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
index 5c4fd13..3d45915 100644
--- a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
+++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
@@ -234,7 +234,7 @@
break;
}
}
- task.setInitialBounds(proposal);
+ task.updateOverrideConfiguration(proposal);
}
private boolean shiftedToFar(Rect start, int shiftPolicy) {
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 9cbaec5..8f10f08 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -52,6 +52,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
+import android.util.DisplayMetrics;
import android.util.Slog;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.util.XmlUtils;
@@ -63,6 +64,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Objects;
final class TaskRecord {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
@@ -271,8 +273,7 @@
long _firstActiveTime, long _lastActiveTime, long lastTimeMoved,
boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
- int callingUid, String callingPackage, boolean resizeable, boolean privileged,
- Rect bounds) {
+ int callingUid, String callingPackage, boolean resizeable, boolean privileged) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
@@ -309,7 +310,6 @@
mCallingPackage = callingPackage;
mResizeable = resizeable;
mPrivileged = privileged;
- mBounds = mLastNonFullscreenBounds = bounds;
}
void touchActiveTime() {
@@ -1163,7 +1163,8 @@
autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
- callingUid, callingPackage, resizeable, privileged, bounds);
+ callingUid, callingPackage, resizeable, privileged);
+ task.updateOverrideConfiguration(bounds);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
activities.get(activityNdx).task = task;
@@ -1173,41 +1174,57 @@
return task;
}
- boolean updateOverrideConfiguration(Configuration newConfig, Rect bounds) {
+ /**
+ * Update task's override configuration based on the bounds.
+ * @return Update configuration or null if there is no change.
+ */
+ Configuration updateOverrideConfiguration(Rect bounds) {
+ if (Objects.equals(mBounds, bounds)) {
+ return null;
+ }
Configuration oldConfig = mOverrideConfig;
- mOverrideConfig = (newConfig == null) ? Configuration.EMPTY : newConfig;
- // We override the configuration only when the task's dimensions are different from the
- // display. In this manner, we know that if the override configuration is empty, the task
- // is necessarily fullscreen.
- mFullscreen = Configuration.EMPTY.equals(mOverrideConfig);
+
+ mFullscreen = bounds == null;
if (mFullscreen) {
if (mBounds != null && stack.mStackId != DOCKED_STACK_ID) {
mLastNonFullscreenBounds = mBounds;
}
mBounds = null;
+ mOverrideConfig = Configuration.EMPTY;
} else {
mBounds = new Rect(bounds);
if (stack.mStackId != DOCKED_STACK_ID) {
mLastNonFullscreenBounds = mBounds;
}
+
+ final Configuration serviceConfig = mService.mConfiguration;
+ mOverrideConfig = new Configuration(serviceConfig);
+ // TODO(multidisplay): Update Dp to that of display stack is on.
+ final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ mOverrideConfig.screenWidthDp =
+ Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
+ mOverrideConfig.screenHeightDp =
+ Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
+ mOverrideConfig.smallestScreenWidthDp =
+ Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
+ mOverrideConfig.orientation =
+ (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
+ ? Configuration.ORIENTATION_PORTRAIT
+ : Configuration.ORIENTATION_LANDSCAPE;
}
- return !mOverrideConfig.equals(oldConfig);
+ return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
}
- /** Returns the stack that should be used to launch this task. */
- int getLaunchStackId(ActivityStack focusStack) {
- if (stack != null) {
- // We are already in a stack silly...
- return stack.mStackId;
- }
- if (isHomeTask()) {
+ /**
+ * Returns the correct stack to use based on task type and currently set bounds,
+ * regardless of the focused stack and current stack association of the task.
+ * The task will be moved (and stack focus changed) later if necessary.
+ */
+ int getLaunchStackId() {
+ if (!isApplicationTask()) {
return HOME_STACK_ID;
}
- if (focusStack != null && focusStack.mStackId != HOME_STACK_ID) {
- // Like it or not you are going in the focused stack!
- return focusStack.mStackId;
- }
- if (mBounds != null || mLastNonFullscreenBounds != null) {
+ if (mBounds != null) {
return FREEFORM_WORKSPACE_STACK_ID;
}
return FULLSCREEN_WORKSPACE_STACK_ID;
@@ -1225,14 +1242,6 @@
return mLastNonFullscreenBounds;
}
- void setInitialBounds(Rect rect) {
- if (mBounds == null) {
- mBounds = new Rect();
- }
- mBounds.set(rect);
- mLastNonFullscreenBounds = mBounds;
- }
-
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..99ca050 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
diff --git a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
index 64b9399..2ccfdd1 100644
--- a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
+++ b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
@@ -71,6 +71,7 @@
// Check we have two IP addresses of the same family.
if (srcAddress == null || dstAddress == null ||
!srcAddress.getClass().getName().equals(dstAddress.getClass().getName())) {
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
}
// Set the protocol.
@@ -102,7 +103,7 @@
InetAddress srcAddress, int srcPort,
InetAddress dstAddress, int dstPort) throws InvalidPacketException {
- if (!(srcAddress instanceof Inet4Address)) {
+ if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index c78f347..90c9ddf 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -62,8 +62,7 @@
private static final String TAG = "KeepaliveTracker";
private static final boolean DBG = true;
- // TODO: Change this to a system-only permission.
- public static final String PERMISSION = android.Manifest.permission.CHANGE_NETWORK_STATE;
+ public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
/** Keeps track of keepalive requests. */
private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
@@ -208,6 +207,8 @@
Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name());
mNai.asyncChannel.sendMessage(CMD_STOP_PACKET_KEEPALIVE, mSlot);
}
+ // TODO: at the moment we unconditionally return failure here. In cases where the
+ // NetworkAgent is alive, should we ask it to reply, so it can return failure?
notifyMessenger(mSlot, reason);
unlinkDeathRecipient();
}
@@ -233,17 +234,14 @@
mKeepalives.put(nai, networkKeepalives);
}
- // Find the lowest-numbered free slot.
+ // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two
+ // separate chipset implementations independently came up with.
int slot;
- for (slot = 0; slot < networkKeepalives.size(); slot++) {
+ for (slot = 1; slot <= networkKeepalives.size(); slot++) {
if (networkKeepalives.get(slot) == null) {
return slot;
}
}
- // No free slot, pick one at the end.
-
- // HACK for broadcom hardware that does not support slot 0!
- if (slot == 0) slot = 1;
return slot;
}
@@ -267,14 +265,15 @@
}
public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
+ String networkName = (nai == null) ? "(null)" : nai.name();
HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
if (networkKeepalives == null) {
- Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + nai.name());
+ Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
return;
}
KeepaliveInfo ki = networkKeepalives.get(slot);
if (ki == null) {
- Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + nai.name());
+ Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName);
return;
}
ki.stop(reason);
@@ -332,6 +331,11 @@
public void startNattKeepalive(NetworkAgentInfo nai, int intervalSeconds, Messenger messenger,
IBinder binder, String srcAddrString, int srcPort, String dstAddrString, int dstPort) {
+ if (nai == null) {
+ notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK);
+ return;
+ }
+
InetAddress srcAddress, dstAddress;
try {
srcAddress = NetworkUtils.numericToInetAddress(srcAddrString);
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/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/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/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 7e46db8..ec7c1c4 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -18,13 +18,23 @@
import android.Manifest;
import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.IUserSwitchObserver;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
import android.os.Binder;
+import android.os.DeadObjectException;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
@@ -51,8 +61,8 @@
import android.hardware.fingerprint.IFingerprintDaemon;
import android.hardware.fingerprint.IFingerprintDaemonCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.view.Display;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.USE_FINGERPRINT;
@@ -60,6 +70,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -79,16 +90,21 @@
private static final String FINGERPRINTD = "android.hardware.fingerprint.IFingerprintDaemon";
private static final int MSG_USER_SWITCHING = 10;
private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+ private static final String ACTION_LOCKOUT_RESET =
+ "com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
private ClientMonitor mAuthClient = null;
private ClientMonitor mEnrollClient = null;
private ClientMonitor mRemoveClient = null;
+ private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
+ new ArrayList<>();
private final AppOpsManager mAppOps;
private static final long MS_PER_SEC = 1000;
private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
private static final int MAX_FAILED_ATTEMPTS = 5;
private static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+ private final String mKeyguardPackage;
Handler mHandler = new Handler() {
@Override
@@ -109,9 +125,19 @@
private long mHalDeviceId;
private int mFailedAttempts;
private IFingerprintDaemon mDaemon;
- private PowerManager mPowerManager;
+ private final PowerManager mPowerManager;
+ private final AlarmManager mAlarmManager;
- private final Runnable mLockoutReset = new Runnable() {
+ private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) {
+ resetFailedAttempts();
+ }
+ }
+ };
+
+ private final Runnable mResetFailedAttemptsRunnable = new Runnable() {
@Override
public void run() {
resetFailedAttempts();
@@ -121,8 +147,13 @@
public FingerprintService(Context context) {
super(context);
mContext = context;
+ mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
+ com.android.internal.R.string.config_keyguardComponent)).getPackageName();
mAppOps = context.getSystemService(AppOpsManager.class);
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
+ RESET_FINGERPRINT_LOCKOUT, null /* handler */);
}
@Override
@@ -248,7 +279,21 @@
}
private boolean inLockoutMode() {
- return mFailedAttempts > MAX_FAILED_ATTEMPTS;
+ return mFailedAttempts >= MAX_FAILED_ATTEMPTS;
+ }
+
+ private void scheduleLockoutReset() {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, getLockoutResetIntent());
+ }
+
+ private void cancelLockoutReset() {
+ mAlarmManager.cancel(getLockoutResetIntent());
+ }
+
+ private PendingIntent getLockoutResetIntent() {
+ return PendingIntent.getBroadcast(mContext, 0,
+ new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT);
}
private void resetFailedAttempts() {
@@ -256,17 +301,17 @@
Slog.v(TAG, "Reset fingerprint lockout");
}
mFailedAttempts = 0;
- // If we're asked to reset failed attempts externally (i.e. from Keyguard), the runnable
- // may still be in the queue; remove it.
- mHandler.removeCallbacks(mLockoutReset);
+ // If we're asked to reset failed attempts externally (i.e. from Keyguard), the alarm might
+ // still be pending; remove it.
+ cancelLockoutReset();
+ notifyLockoutResetMonitors();
}
private boolean handleFailedAttempt(ClientMonitor clientMonitor) {
mFailedAttempts++;
- if (mFailedAttempts > MAX_FAILED_ATTEMPTS) {
+ if (inLockoutMode()) {
// Failing multiple times will continue to push out the lockout time.
- mHandler.removeCallbacks(mLockoutReset);
- mHandler.postDelayed(mLockoutReset, FAIL_LOCKOUT_TIMEOUT_MS);
+ scheduleLockoutReset();
if (clientMonitor != null
&& !clientMonitor.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
Slog.w(TAG, "Cannot send lockout message to client");
@@ -292,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);
@@ -372,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)) {
@@ -436,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);
@@ -493,10 +539,67 @@
return false;
}
- private boolean canUseFingerprint(String opPackageName) {
+ private boolean isForegroundActivity(int uid, int pid) {
+ try {
+ List<RunningAppProcessInfo> procs =
+ ActivityManagerNative.getDefault().getRunningAppProcesses();
+ int N = procs.size();
+ for (int i = 0; i < N; i++) {
+ RunningAppProcessInfo proc = procs.get(i);
+ if (proc.pid == pid && proc.uid == uid
+ && proc.importance == IMPORTANCE_FOREGROUND) {
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "am.getRunningAppProcesses() failed");
+ }
+ return false;
+ }
+
+ /**
+ * @param opPackageName name of package for caller
+ * @param foregroundOnly only allow this call while app is in the foreground
+ * @return true if caller can use fingerprint API
+ */
+ private boolean canUseFingerprint(String opPackageName, boolean foregroundOnly) {
checkPermission(USE_FINGERPRINT);
- return mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, Binder.getCallingUid(),
- opPackageName) == AppOpsManager.MODE_ALLOWED;
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ if (opPackageName.equals(mKeyguardPackage)) {
+ return true; // Keyguard is always allowed
+ }
+ if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
+ Slog.w(TAG,"Rejecting " + opPackageName + " ; not a current user or profile");
+ return false;
+ }
+ if (mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, uid, opPackageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
+ return false;
+ }
+ if (foregroundOnly && !isForegroundActivity(uid, pid)) {
+ Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
+ return false;
+ }
+ return true;
+ }
+
+ private void addLockoutResetMonitor(FingerprintServiceLockoutResetMonitor monitor) {
+ if (!mLockoutMonitors.contains(monitor)) {
+ mLockoutMonitors.add(monitor);
+ }
+ }
+
+ private void removeLockoutResetCallback(
+ FingerprintServiceLockoutResetMonitor monitor) {
+ mLockoutMonitors.remove(monitor);
+ }
+
+ private void notifyLockoutResetMonitors() {
+ for (int i = 0; i < mLockoutMonitors.size(); i++) {
+ mLockoutMonitors.get(i).sendLockoutReset();
+ }
}
private class ClientMonitor implements IBinder.DeathRecipient {
@@ -504,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) {
@@ -593,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);
@@ -614,7 +723,7 @@
FingerprintUtils.vibrateFingerprintSuccess(getContext());
}
result |= true; // we have a valid fingerprint
- mLockoutReset.run();
+ resetFailedAttempts();
}
return result;
}
@@ -654,6 +763,36 @@
}
}
+ private class FingerprintServiceLockoutResetMonitor {
+
+ private final IFingerprintServiceLockoutResetCallback mCallback;
+
+ public FingerprintServiceLockoutResetMonitor(
+ IFingerprintServiceLockoutResetCallback callback) {
+ mCallback = callback;
+ }
+
+ public void sendLockoutReset() {
+ if (mCallback != null) {
+ try {
+ mCallback.onLockoutReset(mHalDeviceId);
+ } catch (DeadObjectException e) {
+ Slog.w(TAG, "Death object while invoking onLockoutReset: ", e);
+ mHandler.post(mRemoveCallbackRunnable);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
+ }
+ }
+ }
+
+ private final Runnable mRemoveCallbackRunnable = new Runnable() {
+ @Override
+ public void run() {
+ removeLockoutResetCallback(FingerprintServiceLockoutResetMonitor.this);
+ }
+ };
+ }
+
private IFingerprintDaemonCallback mDaemonCallback = new IFingerprintDaemonCallback.Stub() {
@Override
@@ -782,12 +921,8 @@
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
- if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
- Slog.w(TAG, "Can't authenticate non-current user");
- return;
- }
- if (!canUseFingerprint(opPackageName)) {
- Slog.w(TAG, "Calling not granted permission to use fingerprint");
+ if (!canUseFingerprint(opPackageName, true /* foregroundOnly */)) {
+ if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
return;
}
@@ -800,14 +935,15 @@
@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);
}
});
}
@Override // Binder call
public void cancelAuthentication(final IBinder token, String opPackageName) {
- if (!canUseFingerprint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
return;
}
mHandler.post(new Runnable() {
@@ -838,7 +974,7 @@
@Override // Binder call
public boolean isHardwareDetected(long deviceId, String opPackageName) {
- if (!canUseFingerprint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
return false;
}
return mHalDeviceId != 0;
@@ -862,7 +998,7 @@
@Override // Binder call
public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
- if (!canUseFingerprint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
return Collections.emptyList();
}
int effectiveUserId = getEffectiveUserId(userId);
@@ -872,7 +1008,7 @@
@Override // Binder call
public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
- if (!canUseFingerprint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
return false;
}
@@ -922,7 +1058,19 @@
public void resetTimeout(byte [] token) {
checkPermission(RESET_FINGERPRINT_LOCKOUT);
// TODO: confirm security token when we move timeout management into the HAL layer.
- mLockoutReset.run();
+ mHandler.post(mResetFailedAttemptsRunnable);
+ }
+
+ @Override
+ public void addLockoutResetCallback(final IFingerprintServiceLockoutResetCallback callback)
+ throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ addLockoutResetMonitor(
+ new FingerprintServiceLockoutResetMonitor(callback));
+ }
+ });
}
}
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/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 17b4f9c..5a13672 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.input;
import android.view.Display;
+import com.android.internal.os.SomeArgs;
import com.android.internal.R;
import com.android.internal.util.XmlUtils;
import com.android.server.DisplayThread;
@@ -52,6 +53,7 @@
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
+import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
import android.os.Binder;
@@ -93,6 +95,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import libcore.io.Streams;
import libcore.util.Objects;
@@ -112,6 +115,7 @@
private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
+ private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 6;
// Pointer to native input manager service object.
private final long mPtr;
@@ -124,6 +128,13 @@
private boolean mSystemReady;
private NotificationManager mNotificationManager;
+ private final Object mTabletModeLock = new Object();
+ // List of currently registered tablet mode changed listeners by process id
+ private final SparseArray<TabletModeChangedListenerRecord> mTabletModeChangedListeners =
+ new SparseArray<>(); // guarded by mTabletModeLock
+ private final List<TabletModeChangedListenerRecord> mTempTabletModeChangedListenersToNotify =
+ new ArrayList<>();
+
// Persistent data store. Must be locked each time during use.
private final PersistentDataStore mDataStore = new PersistentDataStore();
@@ -227,6 +238,11 @@
/** Switch code: Lid switch. When set, lid is shut. */
public static final int SW_LID = 0x00;
+ /** Switch code: Tablet mode switch.
+ * When set, the device is in tablet mode (i.e. no keyboard is connected).
+ */
+ public static final int SW_TABLET_MODE = 0x01;
+
/** Switch code: Keypad slide. When set, keyboard is exposed. */
public static final int SW_KEYPAD_SLIDE = 0x0a;
@@ -246,6 +262,7 @@
public static final int SW_CAMERA_LENS_COVER = 0x09;
public static final int SW_LID_BIT = 1 << SW_LID;
+ public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE;
public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
public static final int SW_HEADPHONE_INSERT_BIT = 1 << SW_HEADPHONE_INSERT;
public static final int SW_MICROPHONE_INSERT_BIT = 1 << SW_MICROPHONE_INSERT;
@@ -774,6 +791,57 @@
}
}
+ @Override // Binder call
+ public void registerTabletModeChangedListener(ITabletModeChangedListener listener) {
+ if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE_LISTENER,
+ "registerTabletModeChangedListener()")) {
+ throw new SecurityException("Requires TABLET_MODE_LISTENER permission");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ synchronized (mTabletModeLock) {
+ final int callingPid = Binder.getCallingPid();
+ if (mTabletModeChangedListeners.get(callingPid) != null) {
+ throw new IllegalStateException("The calling process has already registered "
+ + "a TabletModeChangedListener.");
+ }
+ TabletModeChangedListenerRecord record =
+ new TabletModeChangedListenerRecord(callingPid, listener);
+ try {
+ IBinder binder = listener.asBinder();
+ binder.linkToDeath(record, 0);
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ mTabletModeChangedListeners.put(callingPid, record);
+ }
+ }
+
+ private void onTabletModeChangedListenerDied(int pid) {
+ synchronized (mTabletModeLock) {
+ mTabletModeChangedListeners.remove(pid);
+ }
+ }
+
+ // Must be called on handler
+ private void deliverTabletModeChanged(long whenNanos, boolean inTabletMode) {
+ mTempTabletModeChangedListenersToNotify.clear();
+ final int numListeners;
+ synchronized (mTabletModeLock) {
+ numListeners = mTabletModeChangedListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ mTempTabletModeChangedListenersToNotify.add(
+ mTabletModeChangedListeners.valueAt(i));
+ }
+ }
+ for (int i = 0; i < numListeners; i++) {
+ mTempTabletModeChangedListenersToNotify.get(i).notifyTabletModeChanged(
+ whenNanos, inTabletMode);
+ }
+ }
+
// Must be called on handler.
private void showMissingKeyboardLayoutNotification(InputDevice device) {
if (!mKeyboardLayoutNotificationShown) {
@@ -1419,6 +1487,15 @@
mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
switchMask);
}
+
+ if ((switchMask & SW_TABLET_MODE) != 0) {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
+ args.argi2 = (int) (whenNanos >> 32);
+ args.arg1 = Boolean.valueOf((switchValues & SW_TABLET_MODE_BIT) != 0);
+ mHandler.obtainMessage(MSG_DELIVER_TABLET_MODE_CHANGED,
+ args).sendToTarget();
+ }
}
// Native callback.
@@ -1664,6 +1741,12 @@
case MSG_RELOAD_DEVICE_ALIASES:
reloadDeviceAliases();
break;
+ case MSG_DELIVER_TABLET_MODE_CHANGED:
+ SomeArgs args = (SomeArgs) msg.obj;
+ long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
+ boolean inTabletMode = (boolean) args.arg1;
+ deliverTabletModeChanged(whenNanos, inTabletMode);
+ break;
}
}
}
@@ -1755,6 +1838,34 @@
}
}
+ private final class TabletModeChangedListenerRecord implements DeathRecipient {
+ private final int mPid;
+ private final ITabletModeChangedListener mListener;
+
+ public TabletModeChangedListenerRecord(int pid, ITabletModeChangedListener listener) {
+ mPid = pid;
+ mListener = listener;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Tablet mode changed listener for pid " + mPid + " died.");
+ }
+ onTabletModeChangedListenerDied(mPid);
+ }
+
+ public void notifyTabletModeChanged(long whenNanos, boolean inTabletMode) {
+ try {
+ mListener.onTabletModeChanged(whenNanos, inTabletMode);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid +
+ " that tablet mode changed, assuming it died.", ex);
+ binderDied();
+ }
+ }
+ }
+
private final class VibratorToken implements DeathRecipient {
public final int mDeviceId;
public final IBinder mToken;
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 5c60a61..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,11 +432,50 @@
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();
-
if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
+ if (action == null) {
+ return;
+ }
+
if (action.equals(ALARM_WAKEUP)) {
startNavigating(false);
} else if (action.equals(ALARM_TIMEOUT)) {
@@ -444,15 +484,6 @@
checkSmsSuplInit(intent);
} else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
checkWapSuplInit(intent);
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- // 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)
@@ -490,21 +521,27 @@
private void checkSmsSuplInit(Intent intent) {
SmsMessage[] messages = Intents.getMessagesFromIntent(intent);
-
if (messages == null) {
Log.e(TAG, "Message does not exist in the intent.");
return;
}
- for (int i=0; i <messages.length; i++) {
- byte[] supl_init = messages[i].getUserData();
- native_agps_ni_message(supl_init,supl_init.length);
+ for (SmsMessage message : messages) {
+ if (message != null && message.mWrappedSmsMessage != null) {
+ byte[] suplInit = message.getUserData();
+ if (suplInit != null) {
+ native_agps_ni_message(suplInit, suplInit.length);
+ }
+ }
}
}
private void checkWapSuplInit(Intent intent) {
- byte[] supl_init = (byte[]) intent.getExtra("data");
- native_agps_ni_message(supl_init,supl_init.length);
+ byte[] suplInit = intent.getByteArrayExtra("data");
+ if (suplInit == null) {
+ return;
+ }
+ native_agps_ni_message(suplInit,suplInit.length);
}
private void updateLowPowerMode() {
@@ -564,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"
@@ -730,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);
}
}
@@ -810,7 +886,7 @@
// already downloading data
return;
}
- if (!mNetworkAvailable) {
+ if (!isDataNetworkConnected()) {
// try again when network is up
mInjectNtpTimePending = STATE_PENDING_NETWORK;
return;
@@ -878,7 +954,7 @@
// already downloading data
return;
}
- if (!mNetworkAvailable) {
+ if (!isDataNetworkConnected()) {
// try again when network is up
mDownloadXtraDataPending = STATE_PENDING_NETWORK;
return;
@@ -893,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();
}
@@ -1199,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) {
@@ -1526,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");
@@ -1586,6 +1629,10 @@
}
}
+ private void releaseSuplConnection(int connStatus) {
+ sendMessage(RELEASE_SUPL_CONNECTION, connStatus, null /*obj*/);
+ }
+
/**
* called from native code to report NMEA data received
*/
@@ -1911,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);
}
@@ -1980,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();
@@ -1997,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) {
@@ -2017,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);
@@ -2057,7 +2110,6 @@
intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
intentFilter.addAction(ALARM_TIMEOUT);
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -2065,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);
@@ -2129,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 {
@@ -2186,6 +2242,7 @@
return;
}
+ // TODO: replace the use of this deprecated API
boolean result = mConnMgr.requestRouteToHostAddress(
ConnectivityManager.TYPE_MOBILE_SUPL,
mAGpsDataConnectionIpAddr);
@@ -2197,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();
@@ -2332,4 +2444,3 @@
// GNSS Configuration
private static native void native_configuration_update(String configData);
}
-
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index e69dda1..3991489 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -978,13 +978,12 @@
}
// TODO: move to NotificationManager once we can mock it
- // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(
packageName, packageName, tag, 0x0, builder.getNotification(), idReceived,
- UserHandle.USER_OWNER);
+ UserHandle.USER_ALL);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
// ignored; service lives in system_server
@@ -1016,12 +1015,11 @@
PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
// TODO: move to NotificationManager once we can mock it
- // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(packageName, packageName, tag,
- 0x0, builder.getNotification(), idReceived, UserHandle.USER_OWNER);
+ 0x0, builder.getNotification(), idReceived, UserHandle.USER_ALL);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
// ignored; service lives in system_server
@@ -1030,11 +1028,10 @@
private void cancelNotification(String tag) {
// TODO: move to NotificationManager once we can mock it
- // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
mNotifManager.cancelNotificationWithTag(
- packageName, tag, 0x0, UserHandle.USER_OWNER);
+ packageName, tag, 0x0, UserHandle.USER_ALL);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -1418,7 +1415,8 @@
final int policy = readIntAttribute(in, ATTR_POLICY);
// TODO: set for other users during upgrade
- final int uid = UserHandle.getUid(UserHandle.USER_OWNER, appId);
+ // app policy is deprecated so this is only used in pre system user split.
+ final int uid = UserHandle.getUid(UserHandle.USER_SYSTEM, appId);
if (UserHandle.isApp(uid)) {
setUidPolicyUncheckedLocked(uid, policy, false);
} else {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 0a12d5a..cbe61c3 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -46,7 +46,6 @@
import android.service.notification.ZenModeConfig.EventInfo;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
-import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
@@ -149,6 +148,7 @@
mAudioManager.setRingerModeDelegate(mRingerModeDelegate);
}
mHandler.postMetricsTimer();
+ evaluateZenMode("onSystemReady", true);
}
public void onUserSwitched(int user) {
@@ -330,13 +330,14 @@
}
mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config
mConfigs.put(config.user, config);
- if (config.equals(mConfig)) return true;
if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
ZenLog.traceConfig(reason, mConfig, config);
final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
getNotificationPolicy(config));
mConfig = config;
- dispatchOnConfigChanged();
+ if (config.equals(mConfig)) {
+ dispatchOnConfigChanged();
+ }
if (policyChanged){
dispatchOnPolicyChanged();
}
@@ -370,9 +371,7 @@
private boolean evaluateZenMode(String reason, boolean setRingerMode) {
if (DEBUG) Log.d(TAG, "evaluateZenMode");
- final ArraySet<ZenRule> automaticRules = new ArraySet<ZenRule>();
- final int zen = computeZenMode(automaticRules);
- if (zen == mZenMode) return false;
+ final int zen = computeZenMode();
ZenLog.traceSetZenMode(zen, reason);
mZenMode = zen;
updateRingerModeAffectedStreams();
@@ -381,7 +380,9 @@
applyZenToRingerMode();
}
applyRestrictions();
- mHandler.postDispatchOnZenModeChanged();
+ if (zen != mZenMode) {
+ mHandler.postDispatchOnZenModeChanged();
+ }
return true;
}
@@ -391,7 +392,7 @@
}
}
- private int computeZenMode(ArraySet<ZenRule> automaticRulesOut) {
+ private int computeZenMode() {
if (mConfig == null) return Global.ZEN_MODE_OFF;
if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
int zen = Global.ZEN_MODE_OFF;
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/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 7024ec8..8c23648 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -217,8 +217,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);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b25653e..b7a587b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1146,23 +1146,24 @@
// need to do anything. The pending install
// will be processed later on.
if (!mBound) {
- try {
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindMCS",
- System.identityHashCode(params));
- // If this is the only one pending we might
- // have to bind to the service again.
- if (!connectToService()) {
- Slog.e(TAG, "Failed to bind to media container service");
- params.serviceError();
- return;
- } else {
- // Once we bind to the service, the first
- // pending request will be processed.
- mPendingInstalls.add(idx, params);
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
+ System.identityHashCode(mHandler));
+ // If this is the only one pending we might
+ // have to bind to the service again.
+ if (!connectToService()) {
+ Slog.e(TAG, "Failed to bind to media container service");
+ params.serviceError();
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
+ System.identityHashCode(mHandler));
+ if (params.traceMethod != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
+ params.traceCookie);
}
- } finally {
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindMCS",
- System.identityHashCode(params));
+ return;
+ } else {
+ // Once we bind to the service, the first
+ // pending request will be processed.
+ mPendingInstalls.add(idx, params);
}
} else {
mPendingInstalls.add(idx, params);
@@ -1178,6 +1179,8 @@
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
+ System.identityHashCode(mHandler));
}
if (mContainerService == null) {
if (!mBound) {
@@ -1189,6 +1192,11 @@
params.serviceError();
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
+ if (params.traceMethod != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
+ params.traceMethod, params.traceCookie);
+ }
+ return;
}
mPendingInstalls.clear();
} else {
@@ -1197,6 +1205,9 @@
} else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(params));
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
if (params.startCopy()) {
// We are done... look for more work or to
// go idle.
@@ -1225,9 +1236,8 @@
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
- System.identityHashCode(params));
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
@@ -1468,6 +1478,11 @@
Slog.i(TAG, "Observer no longer exists.");
}
}
+ if (args.traceMethod != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,
+ args.traceCookie);
+ }
+ return;
} else {
Slog.e(TAG, "Bogus post-install token " + msg.arg1);
}
@@ -1551,11 +1566,12 @@
state.getInstallArgs().getUser());
}
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+
processPendingInstall(args, ret);
mHandler.sendEmptyMessage(MCS_UNBIND);
}
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER, "pendingVerification", verificationId);
break;
}
case PACKAGE_VERIFIED: {
@@ -1591,8 +1607,10 @@
ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
}
- processPendingInstall(args, ret);
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+ processPendingInstall(args, ret);
mHandler.sendEmptyMessage(MCS_UNBIND);
}
@@ -2301,16 +2319,16 @@
}
updatePermissionsLPw(null, null, updateFlags);
ver.sdkVersion = mSdkVersion;
- // clear only after permissions have been updated
- mExistingSystemPackages.clear();
- mPromoteSystemApps = false;
- // If this is the first boot, and it is a normal boot, then
- // we need to initialize the default preferred apps.
- if (!mRestoredSettings && !onlyCore) {
- mSettings.applyDefaultPreferredAppsLPw(this, UserHandle.USER_OWNER);
- applyFactoryDefaultBrowserLPw(UserHandle.USER_OWNER);
- primeDomainVerificationsLPw(UserHandle.USER_OWNER);
+ // If this is the first boot or an update from pre-M, and it is a normal
+ // boot, then we need to initialize the default preferred apps across
+ // all defined users.
+ if (!onlyCore && (mPromoteSystemApps || !mRestoredSettings)) {
+ for (UserInfo user : sUserManager.getUsers(true)) {
+ mSettings.applyDefaultPreferredAppsLPw(this, user.id);
+ applyFactoryDefaultBrowserLPw(user.id);
+ primeDomainVerificationsLPw(user.id);
+ }
}
// If this is first boot after an OTA, and a normal boot, then
@@ -2328,6 +2346,10 @@
checkDefaultBrowser();
+ // clear only after permissions and other defaults have been updated
+ mExistingSystemPackages.clear();
+ mPromoteSystemApps = false;
+
// All the changes are done during package scanning.
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
@@ -4553,7 +4575,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);
@@ -4645,8 +4667,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,
@@ -4900,8 +4922,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;
}
@@ -4928,8 +4950,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);
}
@@ -4938,17 +4960,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();
@@ -5977,12 +6006,6 @@
*/
if (shouldHideSystemApp) {
synchronized (mPackages) {
- /*
- * We have to grant systems permissions before we hide, because
- * grantPermissions will assume the package update is trying to
- * expand its permissions.
- */
- grantPermissionsLPw(pkg, true, pkg.packageName);
mSettings.disableSystemPackageLPw(pkg.packageName);
}
}
@@ -6264,10 +6287,26 @@
@Override
public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
- return performDexOpt(packageName, instructionSet, false);
+ return performDexOptTraced(packageName, instructionSet, false);
}
- public boolean performDexOpt(String packageName, String instructionSet, boolean backgroundDexopt) {
+ public boolean performDexOpt(
+ String packageName, String instructionSet, boolean backgroundDexopt) {
+ return performDexOptTraced(packageName, instructionSet, backgroundDexopt);
+ }
+
+ private boolean performDexOptTraced(
+ String packageName, String instructionSet, boolean backgroundDexopt) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ try {
+ return performDexOptInternal(packageName, instructionSet, backgroundDexopt);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private boolean performDexOptInternal(
+ String packageName, String instructionSet, boolean backgroundDexopt) {
boolean dexopt = mLazyDexOpt || backgroundDexopt;
boolean updateUsage = !backgroundDexopt; // Don't update usage if this is just a backgroundDexopt
if (!dexopt && !updateUsage) {
@@ -6347,8 +6386,13 @@
synchronized (mInstallLock) {
final String[] instructionSets = new String[] {
getPrimaryInstructionSet(pkg.applicationInfo) };
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+
final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
true /*forceDex*/, false /* defer */, true /* inclDependencies */);
+
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
throw new IllegalStateException("Failed to dexopt: " + res);
}
@@ -7234,19 +7278,24 @@
// We also need to dexopt any apps that are dependent on this library. Note that
// if these fail, we should abort the install since installing the library will
// result in some apps being broken.
- if (clientLibPkgs != null) {
- if ((scanFlags & SCAN_NO_DEX) == 0) {
- for (int i = 0; i < clientLibPkgs.size(); i++) {
- PackageParser.Package clientPkg = clientLibPkgs.get(i);
- int result = mPackageDexOptimizer.performDexOpt(clientPkg,
- null /* instruction sets */, forceDex,
- (scanFlags & SCAN_DEFER_DEX) != 0, false);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
- "scanPackageLI failed to dexopt clientLibPkgs");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ try {
+ if (clientLibPkgs != null) {
+ if ((scanFlags & SCAN_NO_DEX) == 0) {
+ for (int i = 0; i < clientLibPkgs.size(); i++) {
+ PackageParser.Package clientPkg = clientLibPkgs.get(i);
+ int result = mPackageDexOptimizer.performDexOpt(clientPkg,
+ null /* instruction sets */, forceDex,
+ (scanFlags & SCAN_DEFER_DEX) != 0, false);
+ if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
+ throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
+ "scanPackageLI failed to dexopt clientLibPkgs");
+ }
}
}
}
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Request the ActivityManager to kill the process(only for existing packages)
@@ -7856,8 +7905,12 @@
ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + adjustedAbi);
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+
int result = mPackageDexOptimizer.performDexOpt(ps.pkg,
null /* instruction sets */, forceDexOpt, deferDexOpt, true);
+
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
ps.primaryCpuAbiString = null;
ps.pkg.applicationInfo.primaryCpuAbi = null;
@@ -9640,16 +9693,24 @@
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
- null, verificationParams, user, packageAbiOverride, null);
+ final InstallParams params = new InstallParams(origin, null, observer, installFlags,
+ installerPackageName, null, verificationParams, user, packageAbiOverride, null);
+ params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
+ msg.obj = params;
+
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser",
+ System.identityHashCode(msg.obj));
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(msg.obj));
+
mHandler.sendMessage(msg);
}
void installStage(String packageName, File stagedDir, String stagedCid,
- IPackageInstallObserver2 observer, PackageInstaller.SessionParams params,
+ IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user) {
- final VerificationParams verifParams = new VerificationParams(null, params.originatingUri,
- params.referrerUri, installerUid, null);
+ final VerificationParams verifParams = new VerificationParams(
+ null, sessionParams.originatingUri, sessionParams.referrerUri, installerUid, null);
verifParams.setInstallerUid(installerUid);
final OriginInfo origin;
@@ -9660,10 +9721,15 @@
}
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(origin, null, observer, params.installFlags,
- installerPackageName, params.volumeUuid, verifParams, user, params.abiOverride,
- params.grantedRuntimePermissions);
+ final InstallParams params = new InstallParams(origin, null, observer,
+ sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
+ verifParams, user, sessionParams.abiOverride,
+ sessionParams.grantedRuntimePermissions);
+ params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
+ msg.obj = params;
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
+ System.identityHashCode(msg.obj));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(msg.obj));
@@ -9992,6 +10058,7 @@
if (DEBUG_INSTALL) {
Slog.v(TAG, "BM finishing package install for " + token);
}
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
final Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
@@ -10288,8 +10355,6 @@
} catch (Exception e) {
Slog.e(TAG, "Exception trying to enqueue restore", e);
doRestore = false;
- } finally {
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
}
} else {
Slog.e(TAG, "Backup Manager not found!");
@@ -10322,6 +10387,8 @@
/** User handle for the user requesting the information or installation. */
private final UserHandle mUser;
+ String traceMethod;
+ int traceCookie;
HandlerParams(UserHandle user) {
mUser = user;
@@ -10331,6 +10398,16 @@
return mUser;
}
+ HandlerParams setTraceMethod(String traceMethod) {
+ this.traceMethod = traceMethod;
+ return this;
+ }
+
+ HandlerParams setTraceCookie(int traceCookie) {
+ this.traceCookie = traceCookie;
+ return this;
+ }
+
final boolean startCopy() {
boolean res;
try {
@@ -10555,7 +10632,6 @@
final String packageAbiOverride;
final String[] grantedRuntimePermissions;
-
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, String volumeUuid,
VerificationParams verificationParams, UserHandle user, String packageAbiOverride,
@@ -10863,7 +10939,7 @@
if (ret == PackageManager.INSTALL_SUCCEEDED
&& mRequiredVerifierPackage != null) {
Trace.asyncTraceBegin(
- TRACE_TAG_PACKAGE_MANAGER, "pendingVerification", verificationId);
+ TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
/*
* Send the intent to the required verification agent,
* but only start the verification timeout after the
@@ -11001,6 +11077,9 @@
final UserHandle user;
final String abiOverride;
final String[] installGrantPermissions;
+ /** If non-null, drop an async trace when the install completes */
+ final String traceMethod;
+ final int traceCookie;
// The list of instruction sets supported by this app. This is currently
// only used during the rmdex() phase to clean up resources. We can get rid of this
@@ -11010,7 +11089,8 @@
InstallArgs(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, String volumeUuid,
ManifestDigest manifestDigest, UserHandle user, String[] instructionSets,
- String abiOverride, String[] installGrantPermissions) {
+ String abiOverride, String[] installGrantPermissions,
+ String traceMethod, int traceCookie) {
this.origin = origin;
this.move = move;
this.installFlags = installFlags;
@@ -11022,6 +11102,8 @@
this.instructionSets = instructionSets;
this.abiOverride = abiOverride;
this.installGrantPermissions = installGrantPermissions;
+ this.traceMethod = traceMethod;
+ this.traceCookie = traceCookie;
}
abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
@@ -11115,7 +11197,8 @@
super(params.origin, params.move, params.observer, params.installFlags,
params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
- params.grantedRuntimePermissions);
+ params.grantedRuntimePermissions,
+ params.traceMethod, params.traceCookie);
if (isFwdLocked()) {
throw new IllegalArgumentException("Forward locking only supported in ASEC");
}
@@ -11124,7 +11207,7 @@
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, null, null, null, null, instructionSets,
- null, null);
+ null, null, null, 0);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
}
@@ -11350,7 +11433,8 @@
super(params.origin, params.move, params.observer, params.installFlags,
params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
- params.grantedRuntimePermissions);
+ params.grantedRuntimePermissions,
+ params.traceMethod, params.traceCookie);
}
/** Existing install */
@@ -11358,7 +11442,7 @@
boolean isExternal, boolean isForwardLocked) {
super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0)
| (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
- instructionSets, null, null);
+ instructionSets, null, null, null, 0);
// Hackily pretend we're still looking at a full code path
if (!fullCodePath.endsWith(RES_FILE_NAME)) {
fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath();
@@ -11375,7 +11459,7 @@
AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
| (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
- instructionSets, null, null);
+ instructionSets, null, null, null, 0);
this.cid = cid;
setMountPath(PackageHelper.getSdDir(cid));
}
@@ -11643,7 +11727,8 @@
super(params.origin, params.move, params.observer, params.installFlags,
params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
- params.grantedRuntimePermissions);
+ params.grantedRuntimePermissions,
+ params.traceMethod, params.traceCookie);
}
int copyApk(IMediaContainerService imcs, boolean temp) {
@@ -12203,7 +12288,9 @@
//note that the new package setting would have already been
//added to mPackages. It hasn't been persisted yet.
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
mSettings.writeLPr();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
@@ -12255,7 +12342,9 @@
mSettings.setInstallerPackageName(pkgName, installerPackageName);
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
//to update install status
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
mSettings.writeLPr();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -12504,9 +12593,13 @@
}
// Run dexopt before old package gets removed, to minimize time when app is unavailable
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+
int result = mPackageDexOptimizer
.performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
false /* defer */, false /* inclDependencies */);
+
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
return;
@@ -13758,7 +13851,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;
@@ -16201,8 +16308,16 @@
final Message msg = mHandler.obtainMessage(INIT_COPY);
final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
- msg.obj = new InstallParams(origin, move, installObserver, installFlags,
+ final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
installerPackageName, volumeUuid, null, user, packageAbiOverride, null);
+ params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
+ msg.obj = params;
+
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "movePackage",
+ System.identityHashCode(msg.obj));
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(msg.obj));
+
mHandler.sendMessage(msg);
}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index c75a1d3..66170d4 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -136,9 +136,6 @@
case "signer":
policies.add(readSignerOrThrow(parser));
break;
- case "default":
- policies.add(readDefaultOrThrow(parser));
- break;
default:
skip(parser);
}
@@ -233,45 +230,6 @@
}
/**
- * Loop over a default element looking for seinfo child tags. A {@link Policy}
- * instance will be created and returned in the process. All other tags encountered
- * will be skipped.
- *
- * @param parser an XmlPullParser object representing a default element.
- * @return the constructed {@link Policy} instance
- * @throws IOException
- * @throws XmlPullParserException
- * @throws IllegalArgumentException if any of the validation checks fail while
- * parsing tag values.
- * @throws IllegalStateException if any of the invariants fail when constructing
- * the {@link Policy} instance.
- */
- private static Policy readDefaultOrThrow(XmlPullParser parser) throws IOException,
- XmlPullParserException {
-
- parser.require(XmlPullParser.START_TAG, null, "default");
- Policy.PolicyBuilder pb = new Policy.PolicyBuilder();
- pb.setAsDefaultPolicy();
-
- while (parser.next() != XmlPullParser.END_TAG) {
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- continue;
- }
-
- String tagName = parser.getName();
- if ("seinfo".equals(tagName)) {
- String seinfo = parser.getAttributeValue(null, "value");
- pb.setGlobalSeinfoOrThrow(seinfo);
- readSeinfo(parser);
- } else {
- skip(parser);
- }
- }
-
- return pb.build();
- }
-
- /**
* Loop over a package element looking for seinfo child tags. If found return the
* value attribute of the seinfo tag, otherwise return null. All other tags encountered
* will be skipped.
@@ -337,35 +295,28 @@
/**
* Applies a security label to a package based on an seinfo tag taken from a matched
- * policy. All signature based policy stanzas are consulted first and, if no match
- * is found, the default policy stanza is then consulted. The security label is
- * attached to the ApplicationInfo instance of the package in the event that a matching
- * policy was found.
+ * policy. All signature based policy stanzas are consulted and, if no match is
+ * found, the default seinfo label of 'default' (set in ApplicationInfo object) is
+ * used. The security label is attached to the ApplicationInfo instance of the package
+ * in the event that a matching policy was found.
*
* @param pkg object representing the package to be labeled.
- * @return boolean which determines whether a non null seinfo label was assigned
- * to the package. A null value simply represents that no policy matched.
*/
- public static boolean assignSeinfoValue(PackageParser.Package pkg) {
+ public static void assignSeinfoValue(PackageParser.Package pkg) {
synchronized (sPolicies) {
for (Policy policy : sPolicies) {
String seinfo = policy.getMatchedSeinfo(pkg);
if (seinfo != null) {
pkg.applicationInfo.seinfo = seinfo;
- if (DEBUG_POLICY_INSTALL) {
- Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
- "seinfo=" + seinfo);
- }
- return true;
+ break;
}
}
}
if (DEBUG_POLICY_INSTALL) {
- Slog.i(TAG, "package (" + pkg.packageName + ") doesn't match any policy; " +
- "seinfo will remain null");
+ Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
+ "seinfo=" + pkg.applicationInfo.seinfo);
}
- return false;
}
/**
@@ -506,30 +457,16 @@
* .build();
* }
* </pre>
- * <p>
- * The following is an example of how to use {@link Policy.PolicyBuilder} to create a
- * default based Policy instance.
- * </p>
- * <pre>
- * {@code
- * Policy policy = new Policy.PolicyBuilder()
- * .setAsDefaultPolicy()
- * .setGlobalSeinfoOrThrow("default")
- * .build();
- * }
- * </pre>
*/
final class Policy {
private final String mSeinfo;
- private final boolean mDefaultStanza;
private final Set<Signature> mCerts;
private final Map<String, String> mPkgMap;
// Use the PolicyBuilder pattern to instantiate
private Policy(PolicyBuilder builder) {
mSeinfo = builder.mSeinfo;
- mDefaultStanza = builder.mDefaultStanza;
mCerts = Collections.unmodifiableSet(builder.mCerts);
mPkgMap = Collections.unmodifiableMap(builder.mPkgMap);
}
@@ -545,15 +482,6 @@
}
/**
- * Return whether this policy object represents a default stanza.
- *
- * @return A boolean indicating if this object represents a default policy stanza.
- */
- public boolean isDefaultStanza() {
- return mDefaultStanza;
- }
-
- /**
* Return whether this policy object contains package name mapping refinements.
*
* @return A boolean indicating if this object has inner package name mappings.
@@ -584,10 +512,6 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- if (mDefaultStanza) {
- sb.append("defaultStanza=true ");
- }
-
for (Signature cert : mCerts) {
sb.append("cert=" + cert.toCharsString().substring(0, 11) + "... ");
}
@@ -609,22 +533,15 @@
* is determined using the following steps:
* </p>
* <ul>
- * <li> If this Policy instance is defined as a default stanza:
- * <ul><li>Return the global seinfo value</li></ul>
+ * <li> All certs used to sign the apk and all certs stored with this policy
+ * instance are tested for set equality. If this fails then null is returned.
* </li>
- * <li> If this Policy instance is defined as a signer stanza:
- * <ul>
- * <li> All certs used to sign the apk and all certs stored with this policy
- * instance are tested for set equality. If this fails then null is returned.
- * </li>
- * <li> If all certs match then an appropriate inner package stanza is
- * searched based on package name alone. If matched, the stored seinfo
- * value for that mapping is returned.
- * </li>
- * <li> If all certs matched and no inner package stanza matches then return
- * the global seinfo value. The returned value can be null in this case.
- * </li>
- * </ul>
+ * <li> If all certs match then an appropriate inner package stanza is
+ * searched based on package name alone. If matched, the stored seinfo
+ * value for that mapping is returned.
+ * </li>
+ * <li> If all certs matched and no inner package stanza matches then return
+ * the global seinfo value. The returned value can be null in this case.
* </li>
* </ul>
* <p>
@@ -636,37 +553,34 @@
* A value of null can also be returned if no match occured.
*/
public String getMatchedSeinfo(PackageParser.Package pkg) {
- if (!mDefaultStanza) {
- // Check for exact signature matches across all certs.
- Signature[] certs = mCerts.toArray(new Signature[0]);
- if (!Signature.areExactMatch(certs, pkg.mSignatures)) {
- return null;
- }
-
- // Check for inner package name matches given that the
- // signature checks already passed.
- String seinfoValue = mPkgMap.get(pkg.packageName);
- if (seinfoValue != null) {
- return seinfoValue;
- }
+ // Check for exact signature matches across all certs.
+ Signature[] certs = mCerts.toArray(new Signature[0]);
+ if (!Signature.areExactMatch(certs, pkg.mSignatures)) {
+ return null;
}
- // Return the global seinfo value (even if it's null).
+ // Check for inner package name matches given that the
+ // signature checks already passed.
+ String seinfoValue = mPkgMap.get(pkg.packageName);
+ if (seinfoValue != null) {
+ return seinfoValue;
+ }
+
+ // Return the global seinfo value.
return mSeinfo;
}
/**
* A nested builder class to create {@link Policy} instances. A {@link Policy}
* class instance represents one valid policy stanza found in a mac_permissions.xml
- * file. A valid policy stanza is defined to be either a signer or default stanza
- * which obeys the rules outlined in external/sepolicy/mac_permissions.xml. The
- * {@link #build} method ensures a set of invariants are upheld enforcing the correct
- * stanza structure before returning a valid Policy object.
+ * file. A valid policy stanza is defined to be a signer stanza which obeys the rules
+ * outlined in external/sepolicy/mac_permissions.xml. The {@link #build} method
+ * ensures a set of invariants are upheld enforcing the correct stanza structure
+ * before returning a valid Policy object.
*/
public static final class PolicyBuilder {
private String mSeinfo;
- private boolean mDefaultStanza;
private final Set<Signature> mCerts;
private final Map<String, String> mPkgMap;
@@ -676,19 +590,6 @@
}
/**
- * Sets this stanza as a default stanza. All policy stanzas are assumed to
- * be signer stanzas unless this method is explicitly called. Default stanzas
- * are treated differently with respect to allowable child tags, ordering and
- * when and how policy decisions are enforced.
- *
- * @return The reference to this PolicyBuilder.
- */
- public PolicyBuilder setAsDefaultPolicy() {
- mDefaultStanza = true;
- return this;
- }
-
- /**
* Adds a signature to the set of certs used for validation checks. The purpose
* being that all contained certs will need to be matched against all certs
* contained with an apk.
@@ -710,11 +611,8 @@
/**
* Set the global seinfo tag for this policy stanza. The global seinfo tag
- * represents the seinfo element that is used in one of two ways depending on
- * its context. When attached to a signer tag the global seinfo represents an
- * assignment when there isn't a further inner package refinement in policy.
- * When used with a default tag, it represents the only allowable assignment
- * value.
+ * when attached to a signer tag represents the assignment when there isn't a
+ * further inner package refinement in policy.
*
* @param seinfo the seinfo value given as a String.
* @return The reference to this PolicyBuilder.
@@ -740,9 +638,7 @@
/**
* Create a package name to seinfo value mapping. Each mapping represents
* the seinfo value that will be assigned to the described package name.
- * These localized mappings allow the global seinfo to be overriden. This
- * mapping provides no value when used in conjunction with a default stanza;
- * enforced through the {@link #build} method.
+ * These localized mappings allow the global seinfo to be overriden.
*
* @param pkgName the android package name given to the app
* @param seinfo the seinfo value that will be assigned to the passed pkgName
@@ -799,51 +695,25 @@
* about the expected structure of a policy stanza.
* Those invariants are:
* </p>
- * <ul>
- * <li> If a default stanza
- * <ul>
- * <li> an attached global seinfo tag must be present </li>
- * <li> no signatures and no package names can be present </li>
- * </ul>
- * </li>
- * <li> If a signer stanza
- * <ul>
- * <li> at least one cert must be found </li>
- * <li> either a global seinfo value is present OR at least one
- * inner package mapping must be present BUT not both. </li>
- * </ul>
- * </li>
- * </ul>
- *
+ * <ul>
+ * <li> at least one cert must be found </li>
+ * <li> either a global seinfo value is present OR at least one
+ * inner package mapping must be present BUT not both. </li>
+ * </ul>
* @return an instance of {@link Policy} with the options set from this builder
* @throws IllegalStateException if an invariant is violated.
*/
public Policy build() {
Policy p = new Policy(this);
- if (p.mDefaultStanza) {
- if (p.mSeinfo == null) {
- String err = "Missing global seinfo tag with default stanza.";
- throw new IllegalStateException(err);
- }
- if (p.mCerts.size() != 0) {
- String err = "Certs not allowed with default stanza.";
- throw new IllegalStateException(err);
- }
- if (!p.mPkgMap.isEmpty()) {
- String err = "Inner package mappings not allowed with default stanza.";
- throw new IllegalStateException(err);
- }
- } else {
- if (p.mCerts.size() == 0) {
- String err = "Missing certs with signer tag. Expecting at least one.";
- throw new IllegalStateException(err);
- }
- if (!(p.mSeinfo == null ^ p.mPkgMap.isEmpty())) {
- String err = "Only seinfo tag XOR package tags are allowed within " +
- "a signer stanza.";
- throw new IllegalStateException(err);
- }
+ if (p.mCerts.isEmpty()) {
+ String err = "Missing certs with signer tag. Expecting at least one.";
+ throw new IllegalStateException(err);
+ }
+ if (!(p.mSeinfo == null ^ p.mPkgMap.isEmpty())) {
+ String err = "Only seinfo tag XOR package tags are allowed within " +
+ "a signer stanza.";
+ throw new IllegalStateException(err);
}
return p;
@@ -858,7 +728,6 @@
* <ul>
* <li> signer stanzas with inner package mappings </li>
* <li> signer stanzas with global seinfo tags </li>
- * <li> default stanza </li>
* </ul>
* This comparison also checks for duplicate entries on the input selectors. Any
* found duplicates will be flagged and can be checked with {@link #foundDuplicate}.
@@ -875,11 +744,6 @@
@Override
public int compare(Policy p1, Policy p2) {
- // Give precedence to signature stanzas over default stanzas
- if (p1.isDefaultStanza() != p2.isDefaultStanza()) {
- return p1.isDefaultStanza() ? 1 : -1;
- }
-
// Give precedence to stanzas with inner package mappings
if (p1.hasInnerPackages() != p2.hasInnerPackages()) {
return p1.hasInnerPackages() ? -1 : 1;
@@ -887,7 +751,7 @@
// Check for duplicate entries
if (p1.getSignatures().equals(p2.getSignatures())) {
- // Checks if default stanza or a signer w/o inner package names
+ // Checks if signer w/o inner package names
if (p1.hasGlobalSeinfo()) {
duplicateFound = true;
Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString());
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 9ddbcca..3053904 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -65,6 +65,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
+import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -117,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;
@@ -125,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;
@@ -265,6 +266,7 @@
WindowManagerFuncs mWindowManagerFuncs;
WindowManagerInternal mWindowManagerInternal;
PowerManager mPowerManager;
+ PowerManagerInternal mPowerManagerInternal;
ActivityManagerInternal mActivityManagerInternal;
DreamManagerInternal mDreamManagerInternal;
IStatusBarService mStatusBarService;
@@ -942,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.
@@ -1335,6 +1344,7 @@
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
// Init display burn-in protection
boolean burnInProtectionEnabled = context.getResources().getBoolean(
@@ -2298,13 +2308,6 @@
PhoneWindow win = new PhoneWindow(context);
win.setIsStartingWindow(true);
- final TypedArray ta = win.getWindowStyle();
- if (ta.getBoolean(
- com.android.internal.R.styleable.Window_windowDisablePreview, false)
- || ta.getBoolean(
- com.android.internal.R.styleable.Window_windowShowWallpaper,false)) {
- return null;
- }
Resources r = context.getResources();
win.setTitle(r.getText(labelRes, nonLocalizedLabel));
@@ -2359,16 +2362,6 @@
wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
view = win.getDecorView();
- if (win.isFloating()) {
- // Whoops, there is no way to display an animation/preview
- // of such a thing! After all that work... let's skip it.
- // (Note that we must do this here because it is in
- // getDecorView() where the theme is evaluated... maybe
- // we should peek the floating attribute from the theme
- // earlier.)
- return null;
- }
-
if (DEBUG_STARTING_WINDOW) Slog.d(
TAG, "Adding starting window for " + packageName
+ " / " + appToken + ": "
@@ -4462,7 +4455,7 @@
if (mAppsToBeHidden.isEmpty()) {
if (dismissKeyguard && !mKeyguardSecure) {
mAppsThatDismissKeyguard.add(appToken);
- } else if (win.isDrawnLw()) {
+ } else if (win.isDrawnLw() || win.hasAppShownWindows()) {
mWinShowWhenLocked = win;
mHideLockScreen = true;
mForceStatusBarFromKeyguard = false;
@@ -4496,7 +4489,8 @@
mWinDismissingKeyguard = win;
mSecureDismissingKeyguard = mKeyguardSecure;
mForceStatusBarFromKeyguard = mShowingLockscreen && mKeyguardSecure;
- } else if (mAppsToBeHidden.isEmpty() && showWhenLocked && win.isDrawnLw()) {
+ } else if (mAppsToBeHidden.isEmpty() && showWhenLocked
+ && (win.isDrawnLw() || win.hasAppShownWindows())) {
if (DEBUG_LAYOUT) Slog.v(TAG,
"Setting mHideLockScreen to true by win " + win);
mHideLockScreen = true;
@@ -5124,6 +5118,15 @@
break;
}
+ case KeyEvent.KEYCODE_SOFT_SLEEP: {
+ result &= ~ACTION_PASS_TO_USER;
+ isWakeKey = false;
+ if (!down) {
+ mPowerManagerInternal.setUserInactiveOverrideFromWindowManager();
+ }
+ break;
+ }
+
case KeyEvent.KEYCODE_WAKEUP: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = true;
@@ -6632,6 +6635,11 @@
if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
}
+
+ if (mUiMode == Configuration.UI_MODE_TYPE_CAR) {
+ tmpVisibility |= StatusBarManager.DISABLE_RECENT;
+ }
+
tmpVisibility = updateLightStatusBarLw(tmpVisibility);
final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int diff = visibility ^ mLastSystemUiFlags;
@@ -7053,5 +7061,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/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 6b45941..7ae3c79 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
@@ -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 7ebb7f8..4e702aa 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -393,6 +393,10 @@
// Use -1 to disable.
private int mScreenBrightnessOverrideFromWindowManager = -1;
+ // The window manager has determined the user to be inactive via other means.
+ // Set this to false to disable.
+ private boolean mUserInactiveOverrideFromWindowManager;
+
// The user activity timeout override from the window manager
// to allow the current foreground activity to override the user activity timeout.
// Use -1 to disable.
@@ -1028,6 +1032,10 @@
mNotifier.onUserActivity(event, uid);
+ if (mUserInactiveOverrideFromWindowManager) {
+ mUserInactiveOverrideFromWindowManager = false;
+ }
+
if (mWakefulness == WAKEFULNESS_ASLEEP
|| mWakefulness == WAKEFULNESS_DOZING
|| (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
@@ -1525,6 +1533,7 @@
final int sleepTimeout = getSleepTimeoutLocked();
final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
+ final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
mUserActivitySummary = 0;
if (mLastUserActivityTime >= mLastWakeTime) {
@@ -1550,6 +1559,7 @@
}
}
}
+
if (mUserActivitySummary == 0) {
if (sleepTimeout >= 0) {
final long anyUserActivity = Math.max(mLastUserActivityTime,
@@ -1565,6 +1575,12 @@
nextTimeout = -1;
}
}
+
+ if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
+ mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+ nextTimeout = -1;
+ }
+
if (mUserActivitySummary != 0 && nextTimeout >= 0) {
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
msg.setAsynchronous(true);
@@ -2494,6 +2510,14 @@
}
}
+ private void setUserInactiveOverrideFromWindowManagerInternal() {
+ synchronized (mLock) {
+ mUserInactiveOverrideFromWindowManager = true;
+ mDirty |= DIRTY_USER_ACTIVITY;
+ updatePowerStateLocked();
+ }
+ }
+
private void setUserActivityTimeoutOverrideFromWindowManagerInternal(long timeoutMillis) {
synchronized (mLock) {
if (mUserActivityTimeoutOverrideFromWindowManager != timeoutMillis) {
@@ -2688,6 +2712,8 @@
+ mScreenBrightnessOverrideFromWindowManager);
pw.println(" mUserActivityTimeoutOverrideFromWindowManager="
+ mUserActivityTimeoutOverrideFromWindowManager);
+ pw.println(" mUserInactiveOverrideFromWindowManager="
+ + mUserInactiveOverrideFromWindowManager);
pw.println(" mTemporaryScreenBrightnessSettingOverride="
+ mTemporaryScreenBrightnessSettingOverride);
pw.println(" mTemporaryScreenAutoBrightnessAdjustmentSettingOverride="
@@ -3492,6 +3518,11 @@
}
@Override
+ public void setUserInactiveOverrideFromWindowManager() {
+ setUserInactiveOverrideFromWindowManagerInternal();
+ }
+
+ @Override
public void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis) {
setUserActivityTimeoutOverrideFromWindowManagerInternal(timeoutMillis);
}
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 de7c07e..192b168 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -122,6 +122,8 @@
public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
/** A window in a task is being animated in-place. */
public static final int TRANSIT_TASK_IN_PLACE = 17;
+ /** An activity is being relaunched (e.g. due to configuration change). */
+ public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;
/** Fraction of animation at which the recents thumbnail stays completely transparent */
private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
@@ -131,6 +133,7 @@
private static final int DEFAULT_APP_TRANSITION_DURATION = 336;
private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 336;
+ private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
private final Context mContext;
private final Handler mH;
@@ -240,10 +243,6 @@
return mNextAppTransition != TRANSIT_UNSET;
}
- boolean isTransitionNone() {
- return mNextAppTransition == TRANSIT_NONE;
- }
-
boolean isTransitionEqual(int transit) {
return mNextAppTransition == transit;
}
@@ -252,7 +251,7 @@
return mNextAppTransition;
}
- void setAppTransition(int transit) {
+ private void setAppTransition(int transit) {
mNextAppTransition = transit;
}
@@ -297,7 +296,7 @@
return mNextAppTransitionScaleUp;
}
- boolean prepare() {
+ private boolean prepare() {
if (!isRunning()) {
mAppTransitionState = APP_STATE_IDLE;
notifyAppTransitionPendingLocked();
@@ -1004,6 +1003,22 @@
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
}
+ private Animation createRelaunchAnimation(int appWidth, int appHeight) {
+ 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);
+ set.addAnimation(translate);
+ set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+ return set;
+ }
+
/**
* @return true if and only if the first frame of the transition can be skipped, i.e. the first
* frame of the transition doesn't change the visuals on screen, so we can start
@@ -1040,6 +1055,8 @@
"applyAnimation voice:"
+ " anim=" + a + " transit=" + appTransitionToString(transit)
+ " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
+ } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
+ a = createRelaunchAnimation(appWidth, appHeight);
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimationRes(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
@@ -1174,7 +1191,7 @@
}
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
- IRemoteCallback startedCallback) {
+ IRemoteCallback startedCallback) {
if (isTransitionSet()) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
mNextAppTransitionPackage = packageName;
@@ -1249,12 +1266,20 @@
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
mNextAppTransitionPackage = null;
+ mDefaultNextAppTransitionAnimationSpec = null;
mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionScaleUp = scaleUp;
for (int i = 0; i < specs.length; i++) {
AppTransitionAnimationSpec spec = specs[i];
if (spec != null) {
mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
+ if (i == 0) {
+ // In full screen mode, the transition code depends on the default spec to
+ // be set.
+ Rect rect = spec.rect;
+ putDefaultNextAppTransitionCoordinates(rect.left, rect.top, rect.width(),
+ rect.height());
+ }
}
}
postAnimationCallback();
@@ -1326,6 +1351,9 @@
case TRANSIT_TASK_OPEN_BEHIND: {
return "TRANSIT_TASK_OPEN_BEHIND";
}
+ case TRANSIT_ACTIVITY_RELAUNCH: {
+ return "TRANSIT_ACTIVITY_RELAUNCH";
+ }
default: {
return "<UNKNOWN>";
}
@@ -1427,4 +1455,34 @@
public void setCurrentUser(int newUserId) {
mCurrentUserId = newUserId;
}
+
+ /**
+ * @return true if transition is not running and should not be skipped, false if transition is
+ * already running
+ */
+ boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent) {
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
+ + " transit=" + appTransitionToString(transit)
+ + " " + this
+ + " alwaysKeepCurrent=" + alwaysKeepCurrent
+ + " Callers=" + Debug.getCallers(3));
+ if (!isTransitionSet() || mNextAppTransition == TRANSIT_NONE) {
+ setAppTransition(transit);
+ } else if (!alwaysKeepCurrent) {
+ if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
+ // Opening a new task always supersedes a close for the anim.
+ setAppTransition(transit);
+ } else if (transit == TRANSIT_ACTIVITY_OPEN
+ && isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) {
+ // Opening a new activity always supersedes a close for the anim.
+ setAppTransition(transit);
+ }
+ }
+ boolean prepared = prepare();
+ if (isTransitionSet()) {
+ mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+ mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
+ }
+ return prepared;
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a210223..ff216c5 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -112,6 +112,18 @@
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.
+ boolean mReplacingWindow;
+ // If true, the replaced window was already requested to be removed.
+ boolean mReplacingRemoveRequested;
+ // Whether the replacement of the window should trigger app transition animation.
+ boolean mAnimateReplacingWindow;
+
AppWindowToken(WindowManagerService _service, IApplicationToken _token,
boolean _voiceInteraction) {
super(_service, _token.asBinder(),
@@ -225,16 +237,24 @@
}
WindowState findMainWindow() {
+ WindowState candidate = null;
int j = windows.size();
while (j > 0) {
j--;
WindowState win = windows.get(j);
if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
|| win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
- return win;
+ // In cases where there are multiple windows, we prefer the non-exiting window. This
+ // happens for example when when replacing windows during an activity relaunch. When
+ // constructing the animation, we want the new window, not the exiting one.
+ if (win.mExiting) {
+ candidate = win;
+ } else {
+ return win;
+ }
}
}
- return null;
+ return candidate;
}
boolean isVisible() {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 99e9fe6..e5e468f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -20,9 +20,11 @@
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 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 +72,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 +83,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. */
@@ -115,6 +118,7 @@
mDisplay = display;
mDisplayId = display.getDisplayId();
display.getDisplayInfo(mDisplayInfo);
+ display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
mService = service;
}
@@ -135,6 +139,10 @@
return mDisplayInfo;
}
+ DisplayMetrics getDisplayMetrics() {
+ return mDisplayMetrics;
+ }
+
/**
* Returns true if the specified UID has access to this display.
*/
@@ -172,6 +180,7 @@
void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
+ mDisplay.getMetrics(mDisplayMetrics);
for (int i = mStacks.size() - 1; i >= 0; --i) {
mStacks.get(i).updateDisplayInfo(null);
}
@@ -232,7 +241,7 @@
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = tasks.get(taskNdx);
task.getBounds(mTmpRect);
- if (mTmpRect.contains(x, y)) {
+ if (task.inFreeformWorkspace() && mTmpRect.contains(x, y)) {
return task.mTaskId;
}
}
@@ -240,17 +249,78 @@
return -1;
}
+ /**
+ * Find the id of the task whose outside touch area (for resizing) (x, y)
+ * falls within. Returns -1 if the touch doesn't fall into a resizing area.
+ */
+ int taskIdForControlPoint(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 -1;
+ }
+ task.getBounds(mTmpRect);
+ mTmpRect.inset(-delta, -delta);
+ if (mTmpRect.contains(x, y)) {
+ mTmpRect.inset(delta, delta);
+ if (!mTmpRect.contains(x, y)) {
+ return task.mTaskId;
+ }
+ // User touched inside the task. No need to look further,
+ // focus transfer will be handled in ACTION_UP.
+ return -1;
+ }
+ }
+ }
+ return -1;
+ }
+
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);
+ final Task task = win.mAppToken != null ? win.getTask() : null;
+ 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) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index e87dcde..1e628ff 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -283,11 +283,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) {
@@ -414,7 +415,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;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b3244ff..4244205 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -64,9 +64,9 @@
public InputMonitor(WindowManagerService service) {
mService = service;
}
-
+
/* Notifies the window manager about a broken input channel.
- *
+ *
* Called by the InputManager.
*/
@Override
@@ -83,10 +83,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
@@ -178,7 +178,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
@@ -257,6 +257,20 @@
}
}
+ 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.
@@ -437,56 +451,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 554af28..cc9efdb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -23,7 +23,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -41,12 +40,12 @@
* when no window animation is driving it. */
private static final int DEFAULT_DIM_DURATION = 200;
- // The amount we divide the height/width of the bounds we are trying to fit the task within
- // when using the 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;
+ // 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();
@@ -84,13 +83,13 @@
// of creating a new object per fullscreen task on a display.
private static final SparseArray<DimLayer> sSharedFullscreenDimLayers = new SparseArray<>();
- Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds) {
+ Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
+ Configuration config) {
mTaskId = taskId;
mStack = stack;
mUserId = userId;
mService = service;
- mOverrideConfig = Configuration.EMPTY;
- setBounds(bounds);
+ setBounds(bounds, config);
}
DisplayContent getDisplayContent() {
@@ -172,43 +171,18 @@
}
}
- /** Fits the tasks within the input bounds adjusting the task bounds as needed.
- * @param bounds Bounds to fit the task within. Nothing is done if null.
- * @return Returns true if the task bounds was adjusted in any way.
- * */
- boolean fitWithinBounds(Rect bounds) {
- if (bounds == null || bounds.contains(mBounds)) {
- return false;
- }
- mTmpRect2.set(mBounds);
-
- if (mBounds.left < bounds.left || mBounds.right > bounds.right) {
- final int maxRight = bounds.right - (bounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
- int horizontalDiff = bounds.left - mBounds.left;
- if ((horizontalDiff < 0 && mBounds.left >= maxRight)
- || (mBounds.left + horizontalDiff >= maxRight)) {
- horizontalDiff = maxRight - mBounds.left;
- }
- mTmpRect2.left += horizontalDiff;
- mTmpRect2.right += horizontalDiff;
- }
-
- if (mBounds.top < bounds.top || mBounds.bottom > bounds.bottom) {
- final int maxBottom = bounds.bottom - (bounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
- int verticalDiff = bounds.top - mBounds.top;
- if ((verticalDiff < 0 && mBounds.top >= maxBottom)
- || (mBounds.top + verticalDiff >= maxBottom)) {
- verticalDiff = maxBottom - mBounds.top;
- }
- mTmpRect2.top += verticalDiff;
- mTmpRect2.bottom += verticalDiff;
- }
-
- return setBounds(mTmpRect2);
- }
-
/** Set the task bounds. Passing in null sets the bounds to fullscreen. */
- boolean setBounds(Rect bounds) {
+ int setBounds(Rect bounds, Configuration config) {
+ if (config == null) {
+ config = Configuration.EMPTY;
+ }
+ if (bounds == null && !Configuration.EMPTY.equals(config)) {
+ throw new IllegalArgumentException("null bounds but non empty configuration: "
+ + config);
+ }
+ if (bounds != null && Configuration.EMPTY.equals(config)) {
+ throw new IllegalArgumentException("non null bounds, but empty configuration");
+ }
boolean oldFullscreen = mFullscreen;
int rotation = Surface.ROTATION_0;
final DisplayContent displayContent = mStack.getDisplayContent();
@@ -223,7 +197,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);
@@ -232,53 +206,37 @@
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();
- updateOverrideConfiguration();
- return true;
+ mOverrideConfig = mFullscreen ? Configuration.EMPTY : config;
+ return boundsChange;
}
void getBounds(Rect out) {
out.set(mBounds);
}
- private void updateOverrideConfiguration() {
- final Configuration serviceConfig = mService.mCurConfiguration;
- if (mFullscreen) {
- mOverrideConfig = Configuration.EMPTY;
- return;
- }
-
- if (mOverrideConfig == Configuration.EMPTY) {
- mOverrideConfig = new Configuration();
- }
-
- // TODO(multidisplay): Update Dp to that of display stack is on.
- final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
- mOverrideConfig.screenWidthDp =
- Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
- mOverrideConfig.screenHeightDp =
- Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
- mOverrideConfig.smallestScreenWidthDp =
- Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
- mOverrideConfig.orientation =
- (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
- ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
- }
-
void updateDisplayInfo(final DisplayContent displayContent) {
if (displayContent == null) {
return;
}
if (mFullscreen) {
- setBounds(null);
+ setBounds(null, Configuration.EMPTY);
return;
}
final int newRotation = displayContent.getDisplayInfo().rotation;
@@ -313,7 +271,7 @@
mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
break;
}
- setBounds(mTmpRect2);
+ setBounds(mTmpRect2, mOverrideConfig);
}
/** Updates the dim layer bounds, recreating it if needed. */
@@ -468,6 +426,10 @@
return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
}
+ boolean inFreeformWorkspace() {
+ return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
@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..7e32b84
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -0,0 +1,433 @@
+/*
+ * 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.content.res.Configuration.ORIENTATION_LANDSCAPE;
+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.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+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 mTaskId;
+ private TaskStack mStack;
+ 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;
+
+ InputChannel mServerChannel;
+ InputChannel mClientChannel;
+ InputApplicationHandle mDragApplicationHandle;
+ InputWindowHandle mDragWindowHandle;
+
+ private final class WindowPositionerEventReceiver extends InputEventReceiver {
+ public WindowPositionerEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @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 {
+ boolean endDrag = false;
+ 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) {
+ notifyMoveLocked(newX, newY);
+ }
+ try {
+ mService.mActivityManager.resizeTask(
+ mTaskId, mWindowDragBounds, true /* resizedByUser */);
+ } catch(RemoteException e) {}
+ } break;
+
+ case MotionEvent.ACTION_UP: {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
+ }
+ endDrag = true;
+ } break;
+
+ case MotionEvent.ACTION_CANCEL: {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
+ }
+ endDrag = true;
+ } break;
+ }
+
+ if (endDrag) {
+ mResizing = false;
+ try {
+ mService.mActivityManager.resizeTask(
+ mTaskId, mWindowDragBounds, true /* resizedByUser */);
+ } 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);
+ 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(
+ mTaskId, createMode, true /*toTop*/);
+ }
+ }
+ 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());
+
+ 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);
+ }
+
+ 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;
+
+ // Resume rotations after a drag.
+ if (WindowManagerService.DEBUG_ORIENTATION) {
+ Slog.d(TAG, "Resuming rotation after re-position");
+ }
+ mService.resumeRotationLocked();
+ }
+
+ boolean isTaskResizing(final Task task) {
+ return mResizing && task != null && mTaskId == task.mTaskId;
+ }
+
+ 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;
+ }
+
+ final Task task = win.getTask();
+ mTaskId = task.mTaskId;
+ mStack = task.mStack;
+ mStartDragX = startX;
+ mStartDragY = startY;
+
+ mService.getTaskBounds(mTaskId, mWindowOriginalBounds);
+ }
+
+ private void 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);
+ final int minSizeX = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
+ final int minSizeY = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
+ 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 - minSizeX);
+ }
+ if ((mCtrlType & CTRL_TOP) != 0) {
+ top = Math.min(top + deltaY, bottom - minSizeY);
+ }
+ if ((mCtrlType & CTRL_RIGHT) != 0) {
+ right = Math.max(left + minSizeX, right + deltaX);
+ }
+ if ((mCtrlType & CTRL_BOTTOM) != 0) {
+ bottom = Math.max(top + minSizeY, bottom + deltaY);
+ }
+ mWindowDragBounds.set(left, top, right, bottom);
+ } else {
+ // This is a moving operation.
+ mWindowDragBounds.set(mWindowOriginalBounds);
+ mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
+ updateDimLayerVisibility((int) x);
+ }
+ }
+
+ 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 (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+ || !mStack.isFullscreen()
+ || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
+ return CTRL_NONE;
+ }
+
+ 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() {
+ 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 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 25a71d9..1e6fab6 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -25,15 +25,14 @@
import android.os.Debug;
import android.os.RemoteException;
import android.util.EventLog;
-import android.util.IntArray;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.DisplayInfo;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
public class TaskStack implements DimLayer.DimLayerUser {
@@ -94,17 +93,22 @@
}
}
+ boolean allowTaskResize() {
+ return mStackId == FREEFORM_WORKSPACE_STACK_ID
+ || mStackId == DOCKED_STACK_ID;
+ }
+
/**
* Set the bounds of the stack and its containing tasks.
- * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
+ * @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 changedTaskIds Output list of Ids of tasks that changed in bounds.
- * @param newTaskConfigs Output list of new Configuation of the tasks that changed.
+ * @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 bounds, boolean resizeTasks, IntArray changedTaskIds,
- List<Configuration> newTaskConfigs) {
- if (!setBounds(bounds)) {
+ boolean setBounds(Rect stackBounds, boolean resizeTasks, SparseArray<Configuration> configs,
+ SparseArray<Rect> taskBounds) {
+ if (!setBounds(stackBounds)) {
return false;
}
@@ -113,20 +117,17 @@
}
// Update bounds of containing tasks.
- final Rect newBounds = mFullscreen ? null : mBounds;
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = mTasks.get(taskNdx);
- if (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.
- if (task.fitWithinBounds(newBounds)) {
- changedTaskIds.add(task.mTaskId);
- newTaskConfigs.add(task.mOverrideConfig);
+ Configuration config = configs.get(task.mTaskId);
+ if (config != null) {
+ Rect bounds = taskBounds.get(task.mTaskId);
+ if (bounds == null) {
+ bounds = stackBounds;
}
- } else if (task.setBounds(newBounds)) {
- changedTaskIds.add(task.mTaskId);
- newTaskConfigs.add(task.mOverrideConfig);
+ task.setBounds(bounds, config);
+ } else {
+ Slog.wtf(TAG, "No config for task: " + task + ", is there a mismatch with AM?");
}
}
return true;
@@ -352,11 +353,13 @@
private static void getInitialDockedStackBounds(
Rect displayRect, Rect outBounds, int stackId) {
// 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 splitHorizontally = displayRect.width() > displayRect.height();
+ final boolean topOrLeftCreateMode =
+ WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ final boolean placeTopOrLeft = (stackId == DOCKED_STACK_ID && topOrLeftCreateMode)
+ || (stackId != DOCKED_STACK_ID && !topOrLeftCreateMode);
outBounds.set(displayRect);
- if (stackId == DOCKED_STACK_ID) {
+ if (placeTopOrLeft) {
if (splitHorizontally) {
outBounds.right = displayRect.centerX();
} else {
@@ -407,13 +410,16 @@
for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
- mService.removeWindowInnerLocked(appWindows.get(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 */);
doAnotherLayoutPass = true;
}
}
}
if (doAnotherLayoutPass) {
- mService.requestTraversalLocked();
+ mService.mWindowPlacerLocked.requestTraversal();
}
if (mStackId == DOCKED_STACK_ID) {
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 d0962f4..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);
@@ -750,7 +751,7 @@
}
boolean adjustWallpaperWindows() {
- mService.mInnerFields.mWallpaperMayChange = false;
+ mService.mWindowPlacerLocked.mWallpaperMayChange = false;
final WindowList windows = mService.getDefaultWindowListLocked();
// First find top-most window that has asked to be on top of the wallpaper;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index e6a1be1..ab1bf20 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -23,11 +23,11 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.wm.WindowManagerService.DEBUG_KEYGUARD;
-import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION;
-import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE;
-import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED;
-import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_ACTION_PENDING;
+import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
+import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
+import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
+import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
import android.content.Context;
import android.os.RemoteException;
@@ -41,8 +41,6 @@
import android.view.animation.Animation;
import android.view.Choreographer;
-import com.android.server.wm.WindowManagerService.LayoutFields;
-
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -59,6 +57,7 @@
final WindowManagerService mService;
final Context mContext;
final WindowManagerPolicy mPolicy;
+ private final WindowSurfacePlacer mWindowPlacerLocked;
/** Is any window animating? */
boolean mAnimating;
@@ -84,8 +83,7 @@
int mBulkUpdateParams = 0;
Object mLastWindowFreezeSource;
- SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators =
- new SparseArray<DisplayContentsAnimator>(2);
+ SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators = new SparseArray<>(2);
boolean mInitialized = false;
@@ -115,6 +113,7 @@
mService = service;
mContext = service.mContext;
mPolicy = service.mPolicy;
+ mWindowPlacerLocked = service.mWindowPlacerLocked;
mAnimationFrameCallback = new Choreographer.FrameCallback() {
public void doFrame(long frameTimeNs) {
@@ -179,10 +178,12 @@
mAnimating = mAppWindowAnimating = true;
} else if (appAnimator.wasAnimating) {
// stopped animating, do one more pass through the layout
- setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
- "exiting appToken " + appAnimator.mAppToken + " done", displayId);
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "exiting appToken " + appAnimator.mAppToken + " done", displayId);
if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
- "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken);
+ "updateWindowsApps...: done animating exiting "
+ + appAnimator.mAppToken);
}
}
}
@@ -301,7 +302,8 @@
setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2",
+ mWindowPlacerLocked.debugLayoutRepeats(
+ "updateWindowsAndWallpaperLocked 2",
getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
}
}
@@ -315,7 +317,8 @@
setPendingLayoutChanges(displayId,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3",
+ mWindowPlacerLocked.debugLayoutRepeats(
+ "updateWindowsAndWallpaperLocked 3",
getPendingLayoutChanges(displayId));
}
mService.mFocusMayChange = true;
@@ -409,7 +412,8 @@
setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4",
+ mWindowPlacerLocked.debugLayoutRepeats(
+ "updateWindowsAndWallpaperLocked 4",
getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
}
}
@@ -433,7 +437,8 @@
setPendingLayoutChanges(displayId,
WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5",
+ mWindowPlacerLocked.debugLayoutRepeats(
+ "updateWindowsAndWallpaperLocked 5",
getPendingLayoutChanges(displayId));
}
}
@@ -607,7 +612,8 @@
"Setting mOrientationChangeComplete=true because wtoken "
+ wtoken + " numInteresting=" + wtoken.numInterestingWindows
+ " numDrawn=" + wtoken.numDrawnWindows);
- // This will set mOrientationChangeComplete and cause a pass through layout.
+ // This will set mOrientationChangeComplete and cause a pass through
+ // layout.
setAppLayoutChanges(appAnimator,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
"testTokenMayBeDrawnLocked: freezingScreen", displayId);
@@ -737,15 +743,15 @@
boolean doRequest = false;
if (mBulkUpdateParams != 0) {
- doRequest = mService.copyAnimToLayoutParamsLocked();
+ doRequest = mWindowPlacerLocked.copyAnimToLayoutParamsLocked();
}
if (hasPendingLayoutChanges || doRequest) {
- mService.requestTraversalLocked();
+ mWindowPlacerLocked.requestTraversal();
}
if (!mAnimating && wasAnimating) {
- mService.requestTraversalLocked();
+ mWindowPlacerLocked.requestTraversal();
}
if (WindowManagerService.DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
@@ -755,21 +761,21 @@
}
}
- static String bulkUpdateParamsToString(int bulkUpdateParams) {
+ private static String bulkUpdateParamsToString(int bulkUpdateParams) {
StringBuilder builder = new StringBuilder(128);
- if ((bulkUpdateParams & LayoutFields.SET_UPDATE_ROTATION) != 0) {
+ if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
builder.append(" UPDATE_ROTATION");
}
- if ((bulkUpdateParams & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) {
+ if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) {
builder.append(" WALLPAPER_MAY_CHANGE");
}
- if ((bulkUpdateParams & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) {
+ if ((bulkUpdateParams & WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED) != 0) {
builder.append(" FORCE_HIDING_CHANGED");
}
- if ((bulkUpdateParams & LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
+ if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
builder.append(" ORIENTATION_CHANGE_COMPLETE");
}
- if ((bulkUpdateParams & LayoutFields.SET_TURN_ON_SCREEN) != 0) {
+ if ((bulkUpdateParams & WindowSurfacePlacer.SET_TURN_ON_SCREEN) != 0) {
builder.append(" TURN_ON_SCREEN");
}
return builder.toString();
@@ -846,7 +852,8 @@
if (displayId == windows.get(i).getDisplayId()) {
setPendingLayoutChanges(displayId, changes);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats(reason, getPendingLayoutChanges(displayId));
+ mWindowPlacerLocked.debugLayoutRepeats(reason,
+ getPendingLayoutChanges(displayId));
}
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cf690a5..9608a99 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,34 @@
package com.android.server.wm;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+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;
+
+import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
+
import android.Manifest;
import android.animation.ValueAnimator;
import android.app.ActivityManagerNative;
@@ -34,8 +62,6 @@
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -70,7 +96,6 @@
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
-import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -112,11 +137,8 @@
import android.view.WindowManagerPolicy;
import android.view.WindowManagerPolicy.PointerEventListener;
import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-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;
@@ -129,7 +151,6 @@
import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.Watchdog;
-import com.android.server.am.BatteryStatsService;
import com.android.server.input.InputManagerService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.power.ShutdownThread;
@@ -154,38 +175,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_DIM_BEHIND;
-import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
-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.PRIVATE_FLAG_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-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_KEYGUARD_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_SYSTEM_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
-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 {
@@ -219,6 +208,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;
@@ -301,6 +291,10 @@
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
+ // Used to indicate that if there is already a transition set, it should be preserved when
+ // trying to apply a new one.
+ private static final boolean ALWAYS_KEEP_CURRENT = true;
+
final private KeyguardDisableHandler mKeyguardDisableHandler;
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -313,6 +307,7 @@
}
}
};
+ final WindowSurfacePlacer mWindowPlacerLocked;
/**
* Current user when multi-user is enabled. Don't show windows of
@@ -341,8 +336,6 @@
final IActivityManager mActivityManager;
- final IBatteryStats mBatteryStats;
-
final AppOpsManager mAppOps;
final DisplaySettings mDisplaySettings;
@@ -444,7 +437,6 @@
final float[] mTmpFloats = new float[9];
final Rect mTmpContentRect = new Rect();
- private final Rect mTmpStartRect = new Rect();
boolean mDisplayReady;
boolean mSafeMode;
@@ -467,6 +459,8 @@
private boolean mKeyguardWaitingForActivityDrawn;
+ static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+
class RotationWatcher {
IRotationWatcher watcher;
IBinder.DeathRecipient deathRecipient;
@@ -481,7 +475,6 @@
int mSystemDecorLayer = 0;
final Rect mScreenRect = new Rect();
- boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
long mDisplayFreezeTime = 0;
int mLastDisplayFreezeDuration = 0;
@@ -491,7 +484,7 @@
final static int WINDOWS_FREEZING_SCREENS_NONE = 0;
final static int WINDOWS_FREEZING_SCREENS_ACTIVE = 1;
final static int WINDOWS_FREEZING_SCREENS_TIMEOUT = 2;
- private int mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
+ int mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
@@ -609,56 +602,17 @@
// 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.
int mExitAnimId, mEnterAnimId;
- /** Pulled out of performLayoutAndPlaceSurfacesLockedInner in order to refactor into multiple
- * methods. */
- class LayoutFields {
- static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
- static final int SET_FORCE_HIDING_CHANGED = 1 << 2;
- static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3;
- static final int SET_TURN_ON_SCREEN = 1 << 4;
- static final int SET_WALLPAPER_ACTION_PENDING = 1 << 5;
-
- boolean mWallpaperForceHidingChanged = false;
- boolean mWallpaperMayChange = false;
- boolean mOrientationChangeComplete = true;
- Object mLastWindowFreezeSource = null;
- private Session mHoldScreen = null;
- private boolean mObscured = false;
- private boolean mSyswin = false;
- private float mScreenBrightness = -1;
- private float mButtonBrightness = -1;
- private long mUserActivityTimeout = -1;
- private boolean mUpdateRotation = false;
- boolean mWallpaperActionPending = false;
-
- // Set to true when the display contains content to show the user.
- // When false, the display manager may choose to mirror or blank the display.
- boolean mDisplayHasContent = false;
-
- // Only set while traversing the default display based on its content.
- // Affects the behavior of mirroring on secondary displays.
- boolean mObscureApplicationContentOnSecondaryDisplays = false;
-
- float mPreferredRefreshRate = 0;
-
- int mPreferredModeId = 0;
- }
- final LayoutFields mInnerFields = new LayoutFields();
-
boolean mAnimationScheduled;
/** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
* is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
- private int mTransactionSequence;
-
- /** Only do a maximum of 6 repeated layouts. After that quit */
- private int mLayoutRepeatCount;
+ int mTransactionSequence;
final WindowAnimator mAnimator;
@@ -768,7 +722,7 @@
boolean mInTouchMode;
private ViewServer mViewServer;
- private final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
+ final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
boolean mWindowsChanged = false;
public interface WindowChangeListener {
@@ -788,7 +742,7 @@
// List of clients without a transtiton animation that we notify once we are done transitioning
// since they won't be notified through the app window animator.
- private final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
+ final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
/** Listener to notify activity manager about app transitions. */
private final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
@@ -866,6 +820,9 @@
mDisplaySettings = new DisplaySettings();
mDisplaySettings.readSettingsLocked();
+ mWallpaperControllerLocked = new WallpaperController(this);
+ mWindowPlacerLocked = new WindowSurfacePlacer(this);
+
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG));
@@ -902,7 +859,6 @@
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
mActivityManager = ActivityManagerNative.getDefault();
- mBatteryStats = BatteryStatsService.getService();
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
AppOpsManager.OnOpChangedInternalListener opListener =
new AppOpsManager.OnOpChangedInternalListener() {
@@ -951,10 +907,7 @@
SurfaceControl.closeTransaction();
}
- updateCircularDisplayMaskIfNeeded();
showEmulatorDisplayOverlayIfNeeded();
-
- mWallpaperControllerLocked = new WallpaperController(this);
}
public InputMonitor getInputMonitor() {
@@ -1793,6 +1746,7 @@
boolean addToken = false;
WindowToken token = mTokenMap.get(attrs.token);
+ AppWindowToken atoken = null;
if (token == null) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG, "Attempted to add application window with unknown token "
@@ -1827,7 +1781,7 @@
token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
} else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
- AppWindowToken atoken = token.appWindowToken;
+ atoken = token.appWindowToken;
if (atoken == null) {
Slog.w(TAG, "Attempted to add window with non-application token "
+ token + ". Aborting.");
@@ -1839,7 +1793,7 @@
}
if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
// No need for this guy!
- if (localLOGV) Slog.v(
+ if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v(
TAG, "**** NO NEED TO START: " + attrs.getTitle());
return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;
}
@@ -1973,6 +1927,7 @@
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
+ prepareWindowReplacementTransition(atoken);
if (displayContent.isDefaultDisplay) {
mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets,
@@ -2030,6 +1985,33 @@
return res;
}
+ private void prepareWindowReplacementTransition(AppWindowToken atoken) {
+ if (atoken == null || !atoken.mReplacingWindow || !atoken.mAnimateReplacingWindow) {
+ return;
+ }
+ atoken.allDrawn = false;
+ WindowState replacedWindow = null;
+ for (int i = atoken.windows.size() - 1; i >= 0 && replacedWindow == null; i--) {
+ WindowState candidate = atoken.windows.get(i);
+ if (candidate.mExiting) {
+ replacedWindow = candidate;
+ }
+ }
+ if (replacedWindow == null) {
+ // We expect to already receive a request to remove the old window. If it did not
+ // happen, let's just simply add a window.
+ return;
+ }
+ Rect frame = replacedWindow.mFrame;
+ // We treat this as if this activity was opening, so we can trigger the app transition
+ // animation and piggy-back on existing transition animation infrastructure.
+ mOpeningApps.add(atoken);
+ prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT);
+ mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
+ frame.width(), frame.height());
+ executeAppTransition();
+ }
+
/**
* Returns whether screen capture is disabled for all windows of a specific user.
*/
@@ -2118,6 +2100,16 @@
// If the display is frozen, just remove immediately, since the
// animation wouldn't be seen.
if (win.mHasSurface && okToDisplay()) {
+ final AppWindowToken appToken = win.mAppToken;
+ if (appToken != null && appToken.mReplacingWindow) {
+ // This window is going to be replaced. We need to kepp it around until the new one
+ // gets added, then we will get rid of this one.
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Preserving " + win + " until the new one is "
+ + "added");
+ win.mExiting = true;
+ appToken.mReplacingRemoveRequested = true;
+ return;
+ }
// If we are not currently running the exit animation, we
// need to see about starting one.
wasVisible = win.isWinVisibleLw();
@@ -2137,7 +2129,6 @@
mAccessibilityController.onWindowTransitionLocked(win, transit);
}
}
- final AppWindowToken appToken = win.mAppToken;
final boolean isAnimating = win.mWinAnimator.isAnimating();
// The starting window is the last window in this app token and it isn't animating.
// Allow it to be removed now as there is no additional window or animation that will
@@ -2154,7 +2145,7 @@
}
final boolean focusChanged = updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
if (appToken != null) {
appToken.updateReportedVisibilityLocked();
}
@@ -2177,6 +2168,10 @@
}
void removeWindowInnerLocked(WindowState win) {
+ removeWindowInnerLocked(win, true);
+ }
+
+ void removeWindowInnerLocked(WindowState win, boolean performLayout) {
if (win.mRemoved) {
// Nothing to do.
return;
@@ -2268,13 +2263,15 @@
final WindowList windows = win.getWindowList();
if (windows != null) {
windows.remove(win);
- if (!mInLayout) {
+ if (!mWindowPlacerLocked.isInLayout()) {
assignLayersLocked(windows);
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent != null) {
displayContent.layoutNeeded = true;
}
- performLayoutAndPlaceSurfacesLocked();
+ if (performLayout) {
+ mWindowPlacerLocked.performSurfacePlacement();
+ }
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
@@ -2357,7 +2354,7 @@
if (displayContent != null) {
displayContent.layoutNeeded = true;
}
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
} finally {
@@ -2686,7 +2683,7 @@
}
win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
configChanged = updateOrientationFromAppTokensLocked(false);
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
if (toBeDisplayed && win.mIsWallpaper) {
DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
mWallpaperControllerLocked.updateWallpaperOffset(
@@ -2769,6 +2766,7 @@
try {
synchronized (mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "finishDrawingWindow: " + win);
if (win != null && win.mWinAnimator.finishDrawingLocked()) {
if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
getDefaultDisplayContentLocked().pendingLayoutChanges |=
@@ -2778,7 +2776,7 @@
if (displayContent != null) {
displayContent.layoutNeeded = true;
}
- requestTraversalLocked();
+ mWindowPlacerLocked.requestTraversal();
}
}
} finally {
@@ -3019,7 +3017,7 @@
wtoken.hidden = true;
if (changed) {
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
false /*updateInputWindows*/);
}
@@ -3041,8 +3039,8 @@
Binder.restoreCallingIdentity(origId);
}
- private Task createTaskLocked(
- int taskId, int stackId, int userId, AppWindowToken atoken, Rect bounds) {
+ private Task createTaskLocked(int taskId, int stackId, int userId, AppWindowToken atoken,
+ Rect bounds, Configuration config) {
if (DEBUG_STACK) Slog.i(TAG, "createTaskLocked: taskId=" + taskId + " stackId=" + stackId
+ " atoken=" + atoken + " bounds=" + bounds);
final TaskStack stack = mStackIdToStack.get(stackId);
@@ -3050,17 +3048,17 @@
throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);
}
EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
- Task task = new Task(taskId, stack, userId, this, bounds);
+ Task task = new Task(taskId, stack, userId, this, bounds, config);
mTaskIdToTask.put(taskId, task);
stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */, atoken.showForAllUsers);
return task;
}
@Override
- public Configuration addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
+ 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) {
+ Rect taskBounds, Configuration config, boolean cropWindowsToStack) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3084,7 +3082,7 @@
AppWindowToken atoken = findAppWindowToken(token.asBinder());
if (atoken != null) {
Slog.w(TAG, "Attempted to add existing app token: " + token);
- return null;
+ return;
}
atoken = new AppWindowToken(this, token, voiceInteraction);
atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
@@ -3094,14 +3092,13 @@
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);
Task task = mTaskIdToTask.get(taskId);
- Configuration outConfig = null;
if (task == null) {
- task = createTaskLocked(taskId, stackId, userId, atoken, taskBounds);
- outConfig = task.mOverrideConfig;
+ task = createTaskLocked(taskId, stackId, userId, atoken, taskBounds, config);
}
task.addAppToken(addPos, atoken);
@@ -3110,13 +3107,11 @@
// Application tokens start out hidden.
atoken.hidden = true;
atoken.hiddenRequested = true;
-
- return outConfig;
}
}
@Override
- public Configuration setAppTask(IBinder token, int taskId, Rect taskBounds) {
+ public void setAppTask(IBinder token, int taskId, Rect taskBounds, Configuration config) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppTask()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3126,20 +3121,18 @@
final AppWindowToken atoken = findAppWindowToken(token);
if (atoken == null) {
Slog.w(TAG, "Attempted to set task id of non-existing app token: " + token);
- return null;
+ return;
}
final Task oldTask = atoken.mTask;
oldTask.removeAppToken(atoken);
Task newTask = mTaskIdToTask.get(taskId);
- Configuration outConfig = null;
if (newTask == null) {
newTask = createTaskLocked(
- taskId, oldTask.mStack.mStackId, oldTask.mUserId, atoken, taskBounds);
- outConfig = newTask.mOverrideConfig;
+ taskId, oldTask.mStack.mStackId, oldTask.mUserId, atoken, taskBounds,
+ config);
}
newTask.addAppToken(Integer.MAX_VALUE /* at top */, atoken);
- return outConfig;
}
}
@@ -3391,7 +3384,7 @@
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -3471,41 +3464,24 @@
}
}
+ /**
+ * @param transit What kind of transition is happening. Use one of the constants
+ * AppTransition.TRANSIT_*.
+ * @param alwaysKeepCurrent If true and a transition is already set, new transition will NOT
+ * be set.
+ */
@Override
public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"prepareAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
-
synchronized(mWindowMap) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
- + " transit=" + AppTransition.appTransitionToString(transit)
- + " " + mAppTransition
- + " alwaysKeepCurrent=" + alwaysKeepCurrent
- + " Callers=" + Debug.getCallers(3));
- if (!mAppTransition.isTransitionSet() || mAppTransition.isTransitionNone()) {
- mAppTransition.setAppTransition(transit);
- } else if (!alwaysKeepCurrent) {
- if (transit == AppTransition.TRANSIT_TASK_OPEN
- && mAppTransition.isTransitionEqual(
- AppTransition.TRANSIT_TASK_CLOSE)) {
- // Opening a new task always supersedes a close for the anim.
- mAppTransition.setAppTransition(transit);
- } else if (transit == AppTransition.TRANSIT_ACTIVITY_OPEN
- && mAppTransition.isTransitionEqual(
- AppTransition.TRANSIT_ACTIVITY_CLOSE)) {
- // Opening a new activity always supersedes a close for the anim.
- mAppTransition.setAppTransition(transit);
- }
- }
- if (okToDisplay() && mAppTransition.prepare()) {
+ boolean prepared = mAppTransition.prepareAppTransitionLocked(
+ transit, alwaysKeepCurrent);
+ if (prepared && okToDisplay()) {
mSkipAppTransitionAnimation = false;
}
- if (mAppTransition.isTransitionSet()) {
- mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
- mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, 5000);
- }
}
}
@@ -3589,7 +3565,7 @@
mAppTransition.setReady();
final long origId = Binder.clearCallingIdentity();
try {
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3629,108 +3605,8 @@
return;
}
- if (transferFrom != null) {
- AppWindowToken ttoken = findAppWindowToken(transferFrom);
- if (ttoken != null) {
- WindowState startingWindow = ttoken.startingWindow;
- if (startingWindow != null) {
- // In this case, the starting icon has already been displayed, so start
- // letting windows get shown immediately without any more transitions.
- mSkipAppTransitionAnimation = true;
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG,
- "Moving existing starting " + startingWindow + " from " + ttoken
- + " to " + wtoken);
- final long origId = Binder.clearCallingIdentity();
-
- // Transfer the starting window over to the new token.
- wtoken.startingData = ttoken.startingData;
- wtoken.startingView = ttoken.startingView;
- wtoken.startingDisplayed = ttoken.startingDisplayed;
- ttoken.startingDisplayed = false;
- wtoken.startingWindow = startingWindow;
- wtoken.reportedVisible = ttoken.reportedVisible;
- ttoken.startingData = null;
- ttoken.startingView = null;
- ttoken.startingWindow = null;
- ttoken.startingMoved = true;
- startingWindow.mToken = wtoken;
- startingWindow.mRootToken = wtoken;
- startingWindow.mAppToken = wtoken;
-
- if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "Removing starting window: " + startingWindow);
- }
- startingWindow.getWindowList().remove(startingWindow);
- mWindowsChanged = true;
- if (DEBUG_ADD_REMOVE) Slog.v(TAG,
- "Removing starting " + startingWindow + " from " + ttoken);
- ttoken.windows.remove(startingWindow);
- ttoken.allAppWindows.remove(startingWindow);
- addWindowToListInOrderLocked(startingWindow, true);
-
- // Propagate other interesting state between the
- // tokens. If the old token is displayed, we should
- // immediately force the new one to be displayed. If
- // it is animating, we need to move that animation to
- // the new one.
- if (ttoken.allDrawn) {
- wtoken.allDrawn = true;
- wtoken.deferClearAllDrawn = ttoken.deferClearAllDrawn;
- }
- if (ttoken.firstWindowDrawn) {
- wtoken.firstWindowDrawn = true;
- }
- if (!ttoken.hidden) {
- wtoken.hidden = false;
- wtoken.hiddenRequested = false;
- wtoken.willBeHidden = false;
- }
- if (wtoken.clientHidden != ttoken.clientHidden) {
- wtoken.clientHidden = ttoken.clientHidden;
- wtoken.sendAppVisibilityToClients();
- }
- ttoken.mAppAnimator.transferCurrentAnimation(
- wtoken.mAppAnimator, startingWindow.mWinAnimator);
-
- updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- true /*updateInputWindows*/);
- getDefaultDisplayContentLocked().layoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
- Binder.restoreCallingIdentity(origId);
- return;
- } else if (ttoken.startingData != null) {
- // The previous app was getting ready to show a
- // starting window, but hasn't yet done so. Steal it!
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG,
- "Moving pending starting from " + ttoken
- + " to " + wtoken);
- wtoken.startingData = ttoken.startingData;
- ttoken.startingData = null;
- ttoken.startingMoved = true;
- Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
- // Note: we really want to do sendMessageAtFrontOfQueue() because we
- // want to process the message ASAP, before any other queued
- // messages.
- mH.sendMessageAtFrontOfQueue(m);
- return;
- }
- final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator;
- final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator;
- if (tAppAnimator.thumbnail != null) {
- // The old token is animating with a thumbnail, transfer
- // that to the new token.
- if (wAppAnimator.thumbnail != null) {
- wAppAnimator.thumbnail.destroy();
- }
- wAppAnimator.thumbnail = tAppAnimator.thumbnail;
- wAppAnimator.thumbnailX = tAppAnimator.thumbnailX;
- wAppAnimator.thumbnailY = tAppAnimator.thumbnailY;
- wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
- wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
- tAppAnimator.thumbnail = null;
- }
- }
+ if (transferStartingWindow(transferFrom, wtoken)) {
+ return;
}
// There is no existing starting window, and the caller doesn't
@@ -3753,30 +3629,28 @@
// pretend like we didn't see that.
return;
}
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Translucent="
- + ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsTranslucent, false)
- + " Floating="
- + ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsFloating, false)
- + " ShowWallpaper="
- + ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowShowWallpaper, false));
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(
+ com.android.internal.R.styleable.Window_windowShowWallpaper, false);
+ final boolean windowDisableStarting = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowDisablePreview, false);
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Translucent=" + windowIsTranslucent
+ + " Floating=" + windowIsFloating
+ + " ShowWallpaper=" + windowShowWallpaper);
if (windowIsTranslucent || (!windowIsTranslucentDefined && windowSwipeToDismiss)) {
return;
}
- if (ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsFloating, false)) {
+ if (windowIsFloating || windowDisableStarting) {
return;
}
- if (ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
+ if (windowShowWallpaper) {
if (mWallpaperControllerLocked.getWallpaperTarget() == null) {
// If this theme is requesting a wallpaper, and the wallpaper
// is not curently visible, then this effectively serves as
@@ -3802,6 +3676,113 @@
}
}
+ private boolean transferStartingWindow(IBinder transferFrom, AppWindowToken wtoken) {
+ if (transferFrom == null) {
+ return false;
+ }
+ AppWindowToken ttoken = findAppWindowToken(transferFrom);
+ if (ttoken == null) {
+ return false;
+ }
+ WindowState startingWindow = ttoken.startingWindow;
+ if (startingWindow != null) {
+ // In this case, the starting icon has already been displayed, so start
+ // letting windows get shown immediately without any more transitions.
+ mSkipAppTransitionAnimation = true;
+
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG,
+ "Moving existing starting " + startingWindow + " from " + ttoken
+ + " to " + wtoken);
+ final long origId = Binder.clearCallingIdentity();
+
+ // Transfer the starting window over to the new token.
+ wtoken.startingData = ttoken.startingData;
+ wtoken.startingView = ttoken.startingView;
+ wtoken.startingDisplayed = ttoken.startingDisplayed;
+ ttoken.startingDisplayed = false;
+ wtoken.startingWindow = startingWindow;
+ wtoken.reportedVisible = ttoken.reportedVisible;
+ ttoken.startingData = null;
+ ttoken.startingView = null;
+ ttoken.startingWindow = null;
+ ttoken.startingMoved = true;
+ startingWindow.mToken = wtoken;
+ startingWindow.mRootToken = wtoken;
+ startingWindow.mAppToken = wtoken;
+
+ if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Removing starting window: " + startingWindow);
+ }
+ startingWindow.getWindowList().remove(startingWindow);
+ mWindowsChanged = true;
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "Removing starting " + startingWindow + " from " + ttoken);
+ ttoken.windows.remove(startingWindow);
+ ttoken.allAppWindows.remove(startingWindow);
+ addWindowToListInOrderLocked(startingWindow, true);
+
+ // Propagate other interesting state between the
+ // tokens. If the old token is displayed, we should
+ // immediately force the new one to be displayed. If
+ // it is animating, we need to move that animation to
+ // the new one.
+ if (ttoken.allDrawn) {
+ wtoken.allDrawn = true;
+ wtoken.deferClearAllDrawn = ttoken.deferClearAllDrawn;
+ }
+ if (ttoken.firstWindowDrawn) {
+ wtoken.firstWindowDrawn = true;
+ }
+ if (!ttoken.hidden) {
+ wtoken.hidden = false;
+ wtoken.hiddenRequested = false;
+ wtoken.willBeHidden = false;
+ }
+ if (wtoken.clientHidden != ttoken.clientHidden) {
+ wtoken.clientHidden = ttoken.clientHidden;
+ wtoken.sendAppVisibilityToClients();
+ }
+ ttoken.mAppAnimator.transferCurrentAnimation(
+ wtoken.mAppAnimator, startingWindow.mWinAnimator);
+
+ updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ true /*updateInputWindows*/);
+ getDefaultDisplayContentLocked().layoutNeeded = true;
+ mWindowPlacerLocked.performSurfacePlacement();
+ Binder.restoreCallingIdentity(origId);
+ return true;
+ } else if (ttoken.startingData != null) {
+ // The previous app was getting ready to show a
+ // starting window, but hasn't yet done so. Steal it!
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Moving pending starting from " + ttoken
+ + " to " + wtoken);
+ wtoken.startingData = ttoken.startingData;
+ ttoken.startingData = null;
+ ttoken.startingMoved = true;
+ Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
+ // Note: we really want to do sendMessageAtFrontOfQueue() because we
+ // want to process the message ASAP, before any other queued
+ // messages.
+ mH.sendMessageAtFrontOfQueue(m);
+ return true;
+ }
+ final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator;
+ final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator;
+ if (tAppAnimator.thumbnail != null) {
+ // The old token is animating with a thumbnail, transfer that to the new token.
+ if (wAppAnimator.thumbnail != null) {
+ wAppAnimator.thumbnail.destroy();
+ }
+ wAppAnimator.thumbnail = tAppAnimator.thumbnail;
+ wAppAnimator.thumbnailX = tAppAnimator.thumbnailX;
+ wAppAnimator.thumbnailY = tAppAnimator.thumbnailY;
+ wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
+ wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
+ tAppAnimator.thumbnail = null;
+ }
+ return false;
+ }
+
public void removeAppStartingWindow(IBinder token) {
synchronized (mWindowMap) {
AppWindowToken wtoken = mTokenMap.get(token).appWindowToken;
@@ -3836,7 +3817,7 @@
if (atoken != null) {
atoken.appFullscreen = toOpaque;
setWindowOpaqueLocked(token, toOpaque);
- requestTraversalLocked();
+ mWindowPlacerLocked.requestTraversal();
}
}
}
@@ -3867,10 +3848,13 @@
}
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 transition animation.
- if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting)) {
+ // 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
+ // transition animation
+ // * or this is an opening app and windows are being replaced.
+ if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
+ (visible && wtoken.mReplacingWindow)) {
boolean changed = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden
@@ -3962,7 +3946,7 @@
if (performLayout) {
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
@@ -4100,7 +4084,7 @@
&& mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "set mOrientationChanging of " + w);
w.mOrientationChanging = true;
- mInnerFields.mOrientationChangeComplete = false;
+ mWindowPlacerLocked.mOrientationChangeComplete = false;
}
w.mLastFreezeDuration = 0;
unfrozeWindows = true;
@@ -4120,7 +4104,7 @@
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
stopFreezingDisplayLocked();
}
@@ -4408,7 +4392,7 @@
}
mInputMonitor.setUpdateInputWindowsNeededLw();
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
mInputMonitor.updateInputWindowsLw(false /*force*/);
//dump();
@@ -4467,6 +4451,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.
@@ -4531,7 +4521,9 @@
}
public void removeStack(int stackId) {
- mStackIdToStack.remove(stackId);
+ synchronized (mWindowMap) {
+ mStackIdToStack.remove(stackId);
+ }
}
public void removeTask(int taskId) {
@@ -4558,7 +4550,7 @@
stack.addTask(task, toTop);
final DisplayContent displayContent = stack.getDisplayContent();
displayContent.layoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -4579,7 +4571,7 @@
task.moveTaskToStack(stack, toTop);
final DisplayContent displayContent = stack.getDisplayContent();
displayContent.layoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -4599,22 +4591,22 @@
* @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 changedTaskIds Output list of Ids of tasks that changed in bounds due to resize.
- * @param newTaskConfigs Output list of new Configuation of the tasks that changed.
+ * @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,
- IntArray changedTaskIds, List<Configuration> newTaskConfigs) {
+ SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) {
synchronized (mWindowMap) {
final TaskStack stack = mStackIdToStack.get(stackId);
if (stack == null) {
throw new IllegalArgumentException("resizeStack: stackId " + stackId
+ " not found.");
}
- if (stack.setBounds(bounds, resizeTasks, changedTaskIds, newTaskConfigs)) {
+ if (stack.setBounds(bounds, resizeTasks, configs, taskBounds)) {
stack.resizeWindows();
stack.getDisplayContent().layoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
return stack.isFullscreen();
}
@@ -4639,7 +4631,7 @@
task.positionTaskInStack(stack, position);
final DisplayContent displayContent = stack.getDisplayContent();
displayContent.layoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -4648,19 +4640,24 @@
* Returns a {@link Configuration} object that contains configurations settings
* that should be overridden due to the operation.
*/
- public Configuration resizeTask(int taskId, Rect bounds) {
+ public void resizeTask(int taskId, Rect bounds, Configuration configuration, boolean relayout) {
synchronized (mWindowMap) {
Task task = mTaskIdToTask.get(taskId);
if (task == null) {
throw new IllegalArgumentException("resizeTask: taskId " + taskId
+ " not found.");
}
- if (task.setBounds(bounds)) {
- task.resizeWindows();
- task.getDisplayContent().layoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
+ final int boundsChanged = task.setBounds(bounds, configuration);
+ if (boundsChanged != Task.BOUNDS_CHANGE_NONE) {
+ if ((boundsChanged & Task.BOUNDS_CHANGE_SIZE) == Task.BOUNDS_CHANGE_SIZE) {
+ task.resizeWindows();
+ }
+
+ if (relayout) {
+ task.getDisplayContent().layoutNeeded = true;
+ mWindowPlacerLocked.performSurfacePlacement();
+ }
}
- return new Configuration(task.mOverrideConfig);
}
}
@@ -4835,7 +4832,7 @@
mAnimator.mKeyguardGoingAway = true;
mAnimator.mKeyguardGoingAwayToNotificationShade = keyguardGoingToNotificationShade;
mAnimator.mKeyguardGoingAwayDisableWindowAnimations = disableWindowAnimations;
- requestTraversalLocked();
+ mWindowPlacerLocked.requestTraversal();
}
}
@@ -5051,7 +5048,7 @@
displayContent.switchUserStacks();
rebuildAppWindowListLocked(displayContent);
}
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -5310,7 +5307,7 @@
}
}
- public void updateCircularDisplayMaskIfNeeded() {
+ private void updateCircularDisplayMaskIfNeeded() {
// we're fullscreen and not hosted in an ActivityView
if (mContext.getResources().getConfiguration().isScreenRound()
&& mContext.getResources().getBoolean(
@@ -5638,7 +5635,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();
@@ -5890,7 +5887,7 @@
changed = updateRotationUncheckedLocked(false);
if (!changed || forceRelayout) {
getDefaultDisplayContentLocked().layoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -6024,7 +6021,7 @@
if (w.mHasSurface) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);
w.mOrientationChanging = true;
- mInnerFields.mOrientationChangeComplete = false;
+ mWindowPlacerLocked.mOrientationChangeComplete = false;
}
w.mLastFreezeDuration = 0;
}
@@ -6835,6 +6832,88 @@
}
}
+ boolean startMovingTask(IWindow window, float startX, float startY) {
+ WindowState callingWin = null;
+ synchronized (mWindowMap) {
+ callingWin = windowForClientLocked(null, window, false);
+ if (!startPositioningLocked(callingWin, false /*resize*/, startX, startY)) {
+ return false;
+ }
+ }
+ try {
+ mActivityManager.setFocusedTask(callingWin.getTask().mTaskId);
+ } catch(RemoteException e) {}
+ return true;
+ }
+
+ private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
+ int taskId = -1;
+ AppWindowToken atoken = null;
+ synchronized (mWindowMap) {
+ taskId = displayContent.taskIdForControlPoint(startX, startY);
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null || task.mAppTokens == null) {
+ return;
+ }
+ AppTokenList tokens = task.mAppTokens;
+ atoken = tokens.get(tokens.size() - 1);
+ WindowState win = atoken.findMainWindow();
+ if (!startPositioningLocked(win, true /*resize*/, startX, startY)) {
+ return;
+ }
+ }
+ try {
+ mActivityManager.setFocusedTask(taskId);
+ } 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
// -------------------------------------------------------------
@@ -7020,6 +7099,8 @@
mActivityManager.updateConfiguration(null);
} catch (RemoteException e) {
}
+
+ updateCircularDisplayMaskIfNeeded();
}
private void displayReady(int displayId) {
@@ -7100,6 +7181,9 @@
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;
+
@Override
public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) {
@@ -7174,8 +7258,7 @@
case DO_TRAVERSAL: {
synchronized(mWindowMap) {
- mTraversalScheduled = false;
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
} break;
@@ -7347,7 +7430,7 @@
Slog.w(TAG, "Force clearing orientation change: " + w);
}
}
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
break;
}
@@ -7361,7 +7444,7 @@
+ " mOpeningApps.size()=" + mOpeningApps.size()
+ " mClosingApps.size()=" + mClosingApps.size());
mAppTransition.setTimeout();
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
break;
@@ -7561,6 +7644,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);
@@ -7624,7 +7718,7 @@
case WALLPAPER_DRAW_PENDING_TIMEOUT: {
synchronized (mWindowMap) {
if (mWallpaperControllerLocked.processWallpaperDrawPendingTimeout()) {
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
}
@@ -8016,7 +8110,7 @@
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
private void configureDisplayPolicyLocked(DisplayContent displayContent) {
@@ -8204,7 +8298,7 @@
Arrays.fill(mRebuildTmp, null);
}
- private final void assignLayersLocked(WindowList windows) {
+ final void assignLayersLocked(WindowList windows) {
int N = windows.size();
int curBaseLayer = 0;
int curLayer = 0;
@@ -8274,253 +8368,6 @@
}
}
- private final void performLayoutAndPlaceSurfacesLocked() {
- int loopCount = 6;
- do {
- mTraversalScheduled = false;
- performLayoutAndPlaceSurfacesLockedLoop();
- mH.removeMessages(H.DO_TRAVERSAL);
- loopCount--;
- } while (mTraversalScheduled && loopCount > 0);
- mInnerFields.mWallpaperActionPending = false;
- }
-
- private boolean mInLayout = false;
- private final void performLayoutAndPlaceSurfacesLockedLoop() {
- if (mInLayout) {
- if (DEBUG) {
- throw new RuntimeException("Recursive call!");
- }
- Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers="
- + Debug.getCallers(3));
- return;
- }
-
- if (mWaitingForConfig) {
- // Our configuration has changed (most likely rotation), but we
- // don't yet have the complete configuration to report to
- // applications. Don't do any window layout until we have it.
- return;
- }
-
- if (!mDisplayReady) {
- // Not yet initialized, nothing to do.
- return;
- }
-
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
- mInLayout = true;
-
- boolean recoveringMemory = false;
- if (!mForceRemoves.isEmpty()) {
- recoveringMemory = true;
- // Wait a little bit for things to settle down, and off we go.
- while (!mForceRemoves.isEmpty()) {
- WindowState ws = mForceRemoves.remove(0);
- Slog.i(TAG, "Force removing: " + ws);
- removeWindowInnerLocked(ws);
- }
- Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
- Object tmp = new Object();
- synchronized (tmp) {
- try {
- tmp.wait(250);
- } catch (InterruptedException e) {
- }
- }
- }
-
- try {
- performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
-
- mInLayout = false;
-
- if (needsLayout()) {
- if (++mLayoutRepeatCount < 6) {
- requestTraversalLocked();
- } else {
- Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
- mLayoutRepeatCount = 0;
- }
- } else {
- mLayoutRepeatCount = 0;
- }
-
- if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
- mH.removeMessages(H.REPORT_WINDOWS_CHANGE);
- mH.sendEmptyMessage(H.REPORT_WINDOWS_CHANGE);
- }
- } catch (RuntimeException e) {
- mInLayout = false;
- Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
- }
-
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- }
-
- private final void performLayoutLockedInner(final DisplayContent displayContent,
- boolean initial, boolean updateInputWindows) {
- if (!displayContent.layoutNeeded) {
- return;
- }
- displayContent.layoutNeeded = false;
- WindowList windows = displayContent.getWindowList();
- boolean isDefaultDisplay = displayContent.isDefaultDisplay;
-
- DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int dw = displayInfo.logicalWidth;
- final int dh = displayInfo.logicalHeight;
-
- if (mInputConsumer != null) {
- mInputConsumer.layout(dw, dh);
- }
-
- final int N = windows.size();
- int i;
-
- if (DEBUG_LAYOUT) {
- Slog.v(TAG, "-------------------------------------");
- Slog.v(TAG, "performLayout: needed="
- + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
- }
-
- mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation);
- if (isDefaultDisplay) {
- // Not needed on non-default displays.
- mSystemDecorLayer = mPolicy.getSystemDecorLayerLw();
- mScreenRect.set(0, 0, dw, dh);
- }
-
- mPolicy.getContentRectLw(mTmpContentRect);
- displayContent.resize(mTmpContentRect);
-
- int seq = mLayoutSeq+1;
- if (seq < 0) seq = 0;
- mLayoutSeq = seq;
-
- boolean behindDream = false;
-
- // First perform layout of any root windows (not attached
- // to another window).
- int topAttached = -1;
- for (i = N-1; i >= 0; i--) {
- final WindowState win = windows.get(i);
-
- // Don't do layout of a window if it is not visible, or
- // soon won't be visible, to avoid wasting time and funky
- // changes while a window is animating away.
- final boolean gone = (behindDream && mPolicy.canBeForceHidden(win, win.mAttrs))
- || win.isGoneForLayoutLw();
-
- if (DEBUG_LAYOUT && !win.mLayoutAttached) {
- Slog.v(TAG, "1ST PASS " + win
- + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
- + " mLayoutAttached=" + win.mLayoutAttached
- + " screen changed=" + win.isConfigChanged());
- final AppWindowToken atoken = win.mAppToken;
- if (gone) Slog.v(TAG, " GONE: mViewVisibility="
- + win.mViewVisibility + " mRelayoutCalled="
- + win.mRelayoutCalled + " hidden="
- + win.mRootToken.hidden + " hiddenRequested="
- + (atoken != null && atoken.hiddenRequested)
- + " mAttachedHidden=" + win.mAttachedHidden);
- else Slog.v(TAG, " VIS: mViewVisibility="
- + win.mViewVisibility + " mRelayoutCalled="
- + win.mRelayoutCalled + " hidden="
- + win.mRootToken.hidden + " hiddenRequested="
- + (atoken != null && atoken.hiddenRequested)
- + " mAttachedHidden=" + win.mAttachedHidden);
- }
-
- // If this view is GONE, then skip it -- keep the current
- // frame, and let the caller know so they can ignore it
- // if they want. (We do the normal layout for INVISIBLE
- // windows, since that means "perform layout as normal,
- // just don't display").
- if (!gone || !win.mHaveFrame || win.mLayoutNeeded
- || ((win.isConfigChanged() || win.setInsetsChanged()) &&
- ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
- (win.mHasSurface && win.mAppToken != null &&
- win.mAppToken.layoutConfigChanges)))) {
- if (!win.mLayoutAttached) {
- if (initial) {
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- win.mContentChanged = false;
- }
- if (win.mAttrs.type == TYPE_DREAM) {
- // Don't layout windows behind a dream, so that if it
- // does stuff like hide the status bar we won't get a
- // bad transition when it goes away.
- behindDream = true;
- }
- win.mLayoutNeeded = false;
- win.prelayout();
- mPolicy.layoutWindowLw(win, null);
- win.mLayoutSeq = seq;
- if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame="
- + win.mFrame + " mContainingFrame="
- + win.mContainingFrame + " mDisplayFrame="
- + win.mDisplayFrame);
- } else {
- if (topAttached < 0) topAttached = i;
- }
- }
- }
-
- boolean attachedBehindDream = false;
-
- // Now perform layout of attached windows, which usually
- // depend on the position of the window they are attached to.
- // XXX does not deal with windows that are attached to windows
- // that are themselves attached.
- for (i = topAttached; i >= 0; i--) {
- final WindowState win = windows.get(i);
-
- if (win.mLayoutAttached) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + win
- + " mHaveFrame=" + win.mHaveFrame
- + " mViewVisibility=" + win.mViewVisibility
- + " mRelayoutCalled=" + win.mRelayoutCalled);
- // If this view is GONE, then skip it -- keep the current
- // frame, and let the caller know so they can ignore it
- // if they want. (We do the normal layout for INVISIBLE
- // windows, since that means "perform layout as normal,
- // just don't display").
- if (attachedBehindDream && mPolicy.canBeForceHidden(win, win.mAttrs)) {
- continue;
- }
- if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
- || !win.mHaveFrame || win.mLayoutNeeded) {
- if (initial) {
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- win.mContentChanged = false;
- }
- win.mLayoutNeeded = false;
- win.prelayout();
- mPolicy.layoutWindowLw(win, win.mAttachedWindow);
- win.mLayoutSeq = seq;
- if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame="
- + win.mFrame + " mContainingFrame="
- + win.mContainingFrame + " mDisplayFrame="
- + win.mDisplayFrame);
- }
- } else if (win.mAttrs.type == TYPE_DREAM) {
- // Don't layout windows behind a dream, so that if it
- // does stuff like hide the status bar we won't get a
- // bad transition when it goes away.
- attachedBehindDream = behindDream;
- }
- }
-
- // Window frames may have changed. Tell the input dispatcher about it.
- mInputMonitor.setUpdateInputWindowsNeededLw();
- if (updateInputWindows) {
- mInputMonitor.updateInputWindowsLw(false /*force*/);
- }
-
- mPolicy.finishLayoutLw();
- }
-
void makeWindowFreezingScreenIfNeededLocked(WindowState w) {
// If the screen is currently frozen or off, then keep
// it frozen/off until this window draws at its new
@@ -8529,7 +8376,7 @@
if (DEBUG_ORIENTATION) Slog.v(TAG, "Changing surface while display frozen: " + w);
w.mOrientationChanging = true;
w.mLastFreezeDuration = 0;
- mInnerFields.mOrientationChangeComplete = false;
+ mWindowPlacerLocked.mOrientationChangeComplete = false;
if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
// XXX should probably keep timeout from
@@ -8542,411 +8389,9 @@
}
/**
- * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
- * @param windows List of windows on default display.
* @return bitmap indicating if another pass through layout must be made.
*/
- public int handleAppTransitionReadyLocked(WindowList windows) {
- int appsCount = mOpeningApps.size();
- if (!checkIfTransitionGoodToGo(appsCount)) {
- return 0;
- }
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
- int transit = mAppTransition.getAppTransition();
- if (mSkipAppTransitionAnimation) {
- transit = AppTransition.TRANSIT_UNSET;
- }
- mSkipAppTransitionAnimation = false;
- mNoAnimationNotifyOnTransitionFinished.clear();
-
- mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
-
- rebuildAppWindowListLocked();
-
- mInnerFields.mWallpaperMayChange = false;
-
- // The top-most window will supply the layout params,
- // and we will determine it below.
- LayoutParams animLp = null;
- int bestAnimLayer = -1;
- boolean fullscreenAnim = false;
- boolean voiceInteraction = false;
-
- final WindowState lowerWallpaperTarget =
- mWallpaperControllerLocked.getLowerWallpaperTarget();
- final WindowState upperWallpaperTarget =
- mWallpaperControllerLocked.getUpperWallpaperTarget();
-
- boolean openingAppHasWallpaper = false;
- boolean closingAppHasWallpaper = false;
- final AppWindowToken lowerWallpaperAppToken;
- final AppWindowToken upperWallpaperAppToken;
- if (lowerWallpaperTarget == null) {
- lowerWallpaperAppToken = upperWallpaperAppToken = null;
- } else {
- lowerWallpaperAppToken = lowerWallpaperTarget.mAppToken;
- upperWallpaperAppToken = upperWallpaperTarget.mAppToken;
- }
-
- int i;
- // Do a first pass through the tokens for two
- // things:
- // (1) Determine if both the closing and opening
- // app token sets are wallpaper targets, in which
- // case special animations are needed
- // (since the wallpaper needs to stay static
- // behind them).
- // (2) Find the layout params of the top-most
- // application window in the tokens, which is
- // what will control the animation theme.
- final int closingAppsCount = mClosingApps.size();
- appsCount = closingAppsCount + mOpeningApps.size();
- for (i = 0; i < appsCount; i++) {
- final AppWindowToken wtoken;
- if (i < closingAppsCount) {
- wtoken = mClosingApps.valueAt(i);
- if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
- closingAppHasWallpaper = true;
- }
- } else {
- wtoken = mOpeningApps.valueAt(i - closingAppsCount);
- if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
- openingAppHasWallpaper = true;
- }
- }
-
- voiceInteraction |= wtoken.voiceInteraction;
-
- if (wtoken.appFullscreen) {
- WindowState ws = wtoken.findMainWindow();
- if (ws != null) {
- animLp = ws.mAttrs;
- bestAnimLayer = ws.mLayer;
- fullscreenAnim = true;
- }
- } else if (!fullscreenAnim) {
- WindowState ws = wtoken.findMainWindow();
- if (ws != null) {
- if (ws.mLayer > bestAnimLayer) {
- animLp = ws.mAttrs;
- bestAnimLayer = ws.mLayer;
- }
- }
- }
- }
-
- transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
- closingAppHasWallpaper, lowerWallpaperTarget, upperWallpaperTarget);
-
- // If all closing windows are obscured, then there is
- // no need to do an animation. This is the case, for
- // example, when this transition is being done behind
- // the lock screen.
- if (!mPolicy.allowAppAnimationsLw()) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "Animations disallowed by keyguard or dream.");
- animLp = null;
- }
-
- processApplicationsAnimatingInPlace(transit);
-
- AppWindowToken topClosingApp = null;
- int topClosingLayer = 0;
- appsCount = mClosingApps.size();
- for (i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = mClosingApps.valueAt(i);
- final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
- appAnimator.clearThumbnail();
- appAnimator.animation = null;
- wtoken.inPendingTransaction = false;
- setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction);
- wtoken.updateReportedVisibilityLocked();
- // Force the allDrawn flag, because we want to start
- // this guy's animations regardless of whether it's
- // gotten drawn.
- wtoken.allDrawn = true;
- wtoken.deferClearAllDrawn = false;
- // Ensure that apps that are mid-starting are also scheduled to have their
- // starting windows removed after the animation is complete
- if (wtoken.startingWindow != null && !wtoken.startingWindow.mExiting) {
- scheduleRemoveStartingWindowLocked(wtoken);
- }
- mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
-
- if (animLp != null) {
- int layer = -1;
- for (int j = 0; j < wtoken.windows.size(); j++) {
- WindowState win = wtoken.windows.get(j);
- if (win.mWinAnimator.mAnimLayer > layer) {
- layer = win.mWinAnimator.mAnimLayer;
- }
- }
- if (topClosingApp == null || layer > topClosingLayer) {
- topClosingApp = wtoken;
- topClosingLayer = layer;
- }
- }
- }
-
- AppWindowToken topOpeningApp = null;
- appsCount = mOpeningApps.size();
- for (i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = mOpeningApps.valueAt(i);
- final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
-
- if (!appAnimator.usingTransferredAnimation) {
- appAnimator.clearThumbnail();
- appAnimator.animation = null;
- }
- wtoken.inPendingTransaction = false;
- if (!setTokenVisibilityLocked(
- wtoken, animLp, true, transit, false, voiceInteraction)){
- // This token isn't going to be animating. Add it to the list of tokens to
- // be notified of app transition complete since the notification will not be
- // sent be the app window animator.
- mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
- }
- wtoken.updateReportedVisibilityLocked();
- wtoken.waitingToShow = false;
-
- appAnimator.mAllAppWinAnimators.clear();
- final int windowsCount = wtoken.allAppWindows.size();
- for (int j = 0; j < windowsCount; j++) {
- appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
- }
- mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
- mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
-
- int topOpeningLayer = 0;
- if (animLp != null) {
- int layer = -1;
- for (int j = 0; j < wtoken.windows.size(); j++) {
- WindowState win = wtoken.windows.get(j);
- if (win.mWinAnimator.mAnimLayer > layer) {
- layer = win.mWinAnimator.mAnimLayer;
- }
- }
- if (topOpeningApp == null || layer > topOpeningLayer) {
- topOpeningApp = wtoken;
- topOpeningLayer = layer;
- }
- }
- createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
- }
-
- AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null :
- topOpeningApp.mAppAnimator;
- AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null :
- topClosingApp.mAppAnimator;
-
- mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator);
- mAppTransition.postAnimationCallback();
- mAppTransition.clear();
-
- mOpeningApps.clear();
- mClosingApps.clear();
-
- // This has changed the visibility of windows, so perform
- // a new layout to get them all up-to-date.
- getDefaultDisplayContentLocked().layoutNeeded = true;
-
- // TODO(multidisplay): IMEs are only supported on the default display.
- if (windows == getDefaultWindowListLocked()
- && !moveInputMethodWindowsIfNeededLocked(true)) {
- assignLayersLocked(windows);
- }
- updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, true /*updateInputWindows*/);
- mFocusMayChange = false;
- notifyActivityDrawnForKeyguard();
- return WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT
- | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
-
- }
-
- private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
- boolean closingAppHasWallpaper, WindowState lowerWallpaperTarget,
- WindowState upperWallpaperTarget) {
- // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper
- final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
- final WindowState oldWallpaper = mWallpaperControllerLocked.isWallpaperTargetAnimating()
- ? null : wallpaperTarget;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New wallpaper target=" + wallpaperTarget
- + ", oldWallpaper=" + oldWallpaper
- + ", lower target=" + lowerWallpaperTarget
- + ", upper target=" + upperWallpaperTarget);
- mAnimateWallpaperWithTarget = false;
- if (closingAppHasWallpaper && openingAppHasWallpaper) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
- switch (transit) {
- case AppTransition.TRANSIT_ACTIVITY_OPEN:
- case AppTransition.TRANSIT_TASK_OPEN:
- case AppTransition.TRANSIT_TASK_TO_FRONT:
- transit = AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN;
- break;
- case AppTransition.TRANSIT_ACTIVITY_CLOSE:
- case AppTransition.TRANSIT_TASK_CLOSE:
- case AppTransition.TRANSIT_TASK_TO_BACK:
- transit = AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE;
- break;
- }
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New transit: " + AppTransition.appTransitionToString(transit));
- } else if ((oldWallpaper != null) && !mOpeningApps.isEmpty()
- && !mOpeningApps.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()) {
- // We are transitioning from an activity without
- // a wallpaper to now showing the wallpaper
- transit = AppTransition.TRANSIT_WALLPAPER_OPEN;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New transit into wallpaper: "
- + AppTransition.appTransitionToString(transit));
- } else {
- mAnimateWallpaperWithTarget = true;
- }
- return transit;
- }
-
- private void processApplicationsAnimatingInPlace(int transit) {
- if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) {
- // Find the focused window
- final WindowState win =
- findFocusedWindowLocked(getDefaultDisplayContentLocked());
- if (win != null) {
- final AppWindowToken wtoken = win.mAppToken;
- final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now animating app in place " + wtoken);
- appAnimator.clearThumbnail();
- appAnimator.animation = null;
- updateTokenInPlaceLocked(wtoken, transit);
- wtoken.updateReportedVisibilityLocked();
-
- appAnimator.mAllAppWinAnimators.clear();
- final int N = wtoken.allAppWindows.size();
- for (int j = 0; j < N; j++) {
- appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
- }
- mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
- mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
- }
- }
- }
-
- private boolean checkIfTransitionGoodToGo(int appsCount) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "Checking " + appsCount + " opening apps (frozen="
- + mDisplayFrozen + " timeout="
- + mAppTransition.isTimeout() + ")...");
- if (!mAppTransition.isTimeout()) {
- for (int i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = mOpeningApps.valueAt(i);
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "Check opening app=" + wtoken + ": allDrawn="
- + wtoken.allDrawn + " startingDisplayed="
- + wtoken.startingDisplayed + " startingMoved="
- + wtoken.startingMoved);
- if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
- return false;
- }
- }
-
- // If the wallpaper is visible, we need to check it's ready too.
- return !mWallpaperControllerLocked.isWallpaperVisible() ||
- mWallpaperControllerLocked.wallpaperTransitionReady();
- }
- return true;
- }
-
- private void createThumbnailAppAnimator(int transit, AppWindowToken appToken,
- int openingLayer, int closingLayer) {
- AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator;
- if (openingAppAnimator == null || openingAppAnimator.animation == null) {
- return;
- }
- final int taskId = appToken.mTask.mTaskId;
- Bitmap thumbnailHeader = mAppTransition.getAppTransitionThumbnailHeader(taskId);
- if (thumbnailHeader == null || thumbnailHeader.getConfig() == Config.ALPHA_8) {
- return;
- }
- // This thumbnail animation is very special, we need to have
- // an extra surface with the thumbnail included with the animation.
- Rect dirty = new Rect(0, 0, thumbnailHeader.getWidth(), thumbnailHeader.getHeight());
- try {
- // TODO(multi-display): support other displays
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final Display display = displayContent.getDisplay();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-
- // Create a new surface for the thumbnail
- SurfaceControl surfaceControl = new SurfaceControl(mFxSession,
- "thumbnail anim", dirty.width(), dirty.height(),
- PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
- surfaceControl.setLayerStack(display.getLayerStack());
- if (SHOW_TRANSACTIONS) {
- Slog.i(TAG, " THUMBNAIL " + surfaceControl + ": CREATE");
- }
-
- // Draw the thumbnail onto the surface
- Surface drawSurface = new Surface();
- drawSurface.copyFrom(surfaceControl);
- Canvas c = drawSurface.lockCanvas(dirty);
- c.drawBitmap(thumbnailHeader, 0, 0, null);
- drawSurface.unlockCanvasAndPost(c);
- drawSurface.release();
-
- // Get the thumbnail animation
- Animation anim;
- if (mAppTransition.isNextThumbnailTransitionAspectScaled()) {
- // If this is a multi-window scenario, we use the windows frame as
- // destination of the thumbnail header animation. If this is a full screen
- // window scenario, we use the whole display as the target.
- WindowState win = appToken.findMainWindow();
- Rect appRect = win != null ? win.getContentFrameLw() :
- new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
- // For the new aspect-scaled transition, we want it to always show
- // above the animating opening/closing window, and we want to
- // synchronize its thumbnail surface with the surface for the
- // open/close animation (only on the way down)
- anim = mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
- thumbnailHeader, taskId);
- Log.d(TAG, "assigning thumbnail force above layer: " + openingLayer + " " +
- closingLayer);
- openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
- openingAppAnimator.deferThumbnailDestruction =
- !mAppTransition.isNextThumbnailTransitionScaleUp();
- } else {
- anim = mAppTransition.createThumbnailScaleAnimationLocked(
- displayInfo.appWidth, displayInfo.appHeight, transit, thumbnailHeader);
- }
- anim.restrictDuration(MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(getTransitionAnimationScaleLocked());
-
- openingAppAnimator.thumbnail = surfaceControl;
- openingAppAnimator.thumbnailLayer = openingLayer;
- openingAppAnimator.thumbnailAnimation = anim;
- mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
- openingAppAnimator.thumbnailX = mTmpStartRect.left;
- openingAppAnimator.thumbnailY = mTmpStartRect.top;
- } catch (OutOfResourcesException e) {
- Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w=" + dirty.width()
- + " h=" + dirty.height(), e);
- openingAppAnimator.clearThumbnail();
- }
- }
-
- /**
- * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
- * @return bitmap indicating if another pass through layout must be made.
- */
- private int handleAnimatingStoppedAndTransitionLocked() {
+ int handleAnimatingStoppedAndTransitionLocked() {
int changes = 0;
mAppTransition.setIdle();
@@ -8976,7 +8421,7 @@
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"Wallpaper layer changed: assigning layers + relayout");
moveInputMethodWindowsIfNeededLocked(true);
- mInnerFields.mWallpaperMayChange = true;
+ mWindowPlacerLocked.mWallpaperMayChange = true;
// Since the window list has been rebuilt, focus might
// have to be recomputed since the actual order of windows
// might have changed again.
@@ -8985,7 +8430,7 @@
return changes;
}
- private void updateResizingWindows(final WindowState w) {
+ void updateResizingWindows(final WindowState w) {
final WindowStateAnimator winAnimator = w.mWinAnimator;
if (w.mHasSurface && w.mLayoutSeq == mLayoutSeq) {
w.setInsetsChanged();
@@ -9057,707 +8502,6 @@
}
}
- /**
- * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
- * @param w WindowState this method is applied to.
- * @param innerDw Width of app window.
- * @param innerDh Height of app window.
- */
- private void handleNotObscuredLocked(final WindowState w,
- final int innerDw, final int innerDh) {
- final WindowManager.LayoutParams attrs = w.mAttrs;
- final int attrFlags = attrs.flags;
- final boolean canBeSeen = w.isDisplayedLw();
- final boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
-
- if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) {
- // This window completely covers everything behind it,
- // so we want to leave all of them as undimmed (for
- // performance reasons).
- mInnerFields.mObscured = true;
- }
-
- if (w.mHasSurface) {
- if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
- mInnerFields.mHoldScreen = w.mSession;
- }
- if (!mInnerFields.mSyswin && w.mAttrs.screenBrightness >= 0
- && mInnerFields.mScreenBrightness < 0) {
- mInnerFields.mScreenBrightness = w.mAttrs.screenBrightness;
- }
- if (!mInnerFields.mSyswin && w.mAttrs.buttonBrightness >= 0
- && mInnerFields.mButtonBrightness < 0) {
- mInnerFields.mButtonBrightness = w.mAttrs.buttonBrightness;
- }
- if (!mInnerFields.mSyswin && w.mAttrs.userActivityTimeout >= 0
- && mInnerFields.mUserActivityTimeout < 0) {
- mInnerFields.mUserActivityTimeout = w.mAttrs.userActivityTimeout;
- }
-
- final int type = attrs.type;
- if (canBeSeen
- && (type == TYPE_SYSTEM_DIALOG
- || type == TYPE_SYSTEM_ERROR
- || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0)) {
- mInnerFields.mSyswin = true;
- }
-
- if (canBeSeen) {
- // This function assumes that the contents of the default display are
- // processed first before secondary displays.
- final DisplayContent displayContent = w.getDisplayContent();
- if (displayContent != null && displayContent.isDefaultDisplay) {
- // While a dream or keyguard is showing, obscure ordinary application
- // content on secondary displays (by forcibly enabling mirroring unless
- // there is other content we want to show) but still allow opaque
- // keyguard dialogs to be shown.
- if (type == TYPE_DREAM || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
- mInnerFields.mObscureApplicationContentOnSecondaryDisplays = true;
- }
- mInnerFields.mDisplayHasContent = true;
- } else if (displayContent != null &&
- (!mInnerFields.mObscureApplicationContentOnSecondaryDisplays
- || (mInnerFields.mObscured && type == TYPE_KEYGUARD_DIALOG))) {
- // Allow full screen keyguard presentation dialogs to be seen.
- mInnerFields.mDisplayHasContent = true;
- }
- if (mInnerFields.mPreferredRefreshRate == 0
- && w.mAttrs.preferredRefreshRate != 0) {
- mInnerFields.mPreferredRefreshRate = w.mAttrs.preferredRefreshRate;
- }
- if (mInnerFields.mPreferredModeId == 0
- && w.mAttrs.preferredDisplayModeId != 0) {
- mInnerFields.mPreferredModeId = w.mAttrs.preferredDisplayModeId;
- }
- }
- }
- }
-
- private void handleFlagDimBehind(WindowState w) {
- final WindowManager.LayoutParams attrs = w.mAttrs;
- if ((attrs.flags & FLAG_DIM_BEHIND) != 0
- && w.isDisplayedLw()
- && !w.mExiting) {
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- final Task task = w.getTask();
- if (task == null) {
- return;
- }
- task.setContinueDimming();
- if (!task.isDimming(winAnimator)) {
- if (localLOGV) Slog.v(TAG, "Win " + w + " start dimming.");
- task.startDimmingIfNeeded(winAnimator);
- }
- }
- }
-
- private void updateAllDrawnLocked(DisplayContent displayContent) {
- // See if any windows have been drawn, so they (and others
- // associated with them) can now be shown.
- ArrayList<TaskStack> stacks = displayContent.getStacks();
- for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ArrayList<Task> tasks = stacks.get(stackNdx).getTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
- for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
- final AppWindowToken wtoken = tokens.get(tokenNdx);
- if (!wtoken.allDrawn) {
- int numInteresting = wtoken.numInterestingWindows;
- if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
- if (DEBUG_VISIBILITY) Slog.v(TAG,
- "allDrawn: " + wtoken
- + " interesting=" + numInteresting
- + " drawn=" + wtoken.numDrawnWindows);
- wtoken.allDrawn = true;
- // Force an additional layout pass where WindowStateAnimator#
- // commitFinishDrawingLocked() will call performShowLocked().
- displayContent.layoutNeeded = true;
- mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, wtoken.token).sendToTarget();
- }
- }
- }
- }
- }
- }
-
- // "Something has changed! Let's make it correct now."
- private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) {
- if (DEBUG_WINDOW_TRACE) {
- Slog.v(TAG, "performLayoutAndPlaceSurfacesLockedInner: entry. Called by "
- + Debug.getCallers(3));
- }
-
- int i;
- boolean updateInputWindowsNeeded = false;
-
- if (mFocusMayChange) {
- mFocusMayChange = false;
- updateInputWindowsNeeded = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/);
- }
-
- // Initialize state of exiting tokens.
- final int numDisplays = mDisplayContents.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
- for (i=displayContent.mExitingTokens.size()-1; i>=0; i--) {
- displayContent.mExitingTokens.get(i).hasVisible = false;
- }
- }
-
- for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; --stackNdx) {
- // Initialize state of exiting applications.
- final AppTokenList exitingAppTokens =
- mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
- for (int tokenNdx = exitingAppTokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
- exitingAppTokens.get(tokenNdx).hasVisible = false;
- }
- }
-
- mInnerFields.mHoldScreen = null;
- mInnerFields.mScreenBrightness = -1;
- mInnerFields.mButtonBrightness = -1;
- mInnerFields.mUserActivityTimeout = -1;
- mInnerFields.mObscureApplicationContentOnSecondaryDisplays = false;
-
- mTransactionSequence++;
-
- final DisplayContent defaultDisplay = getDefaultDisplayContentLocked();
- final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
- final int defaultDw = defaultInfo.logicalWidth;
- final int defaultDh = defaultInfo.logicalHeight;
-
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
- SurfaceControl.openTransaction();
- try {
-
- if (mWatermark != null) {
- mWatermark.positionSurface(defaultDw, defaultDh);
- }
- if (mStrictModeFlash != null) {
- mStrictModeFlash.positionSurface(defaultDw, defaultDh);
- }
- if (mCircularDisplayMask != null) {
- mCircularDisplayMask.positionSurface(defaultDw, defaultDh, mRotation);
- }
- if (mEmulatorDisplayOverlay != null) {
- mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh, mRotation);
- }
-
- boolean focusDisplayed = false;
-
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
- boolean updateAllDrawn = false;
- WindowList windows = displayContent.getWindowList();
- DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int displayId = displayContent.getDisplayId();
- final int dw = displayInfo.logicalWidth;
- final int dh = displayInfo.logicalHeight;
- final int innerDw = displayInfo.appWidth;
- final int innerDh = displayInfo.appHeight;
- final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-
- // Reset for each display.
- mInnerFields.mDisplayHasContent = false;
- mInnerFields.mPreferredRefreshRate = 0;
- mInnerFields.mPreferredModeId = 0;
-
- int repeats = 0;
- do {
- repeats++;
- if (repeats > 6) {
- Slog.w(TAG, "Animation repeat aborted after too many iterations");
- displayContent.layoutNeeded = false;
- break;
- }
-
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
- displayContent.pendingLayoutChanges);
-
- if ((displayContent.pendingLayoutChanges &
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0 &&
- mWallpaperControllerLocked.adjustWallpaperWindows()) {
- assignLayersLocked(windows);
- displayContent.layoutNeeded = true;
- }
-
- if (isDefaultDisplay && (displayContent.pendingLayoutChanges
- & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
- if (updateOrientationFromAppTokensLocked(true)) {
- displayContent.layoutNeeded = true;
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
- }
- }
-
- if ((displayContent.pendingLayoutChanges
- & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
- displayContent.layoutNeeded = true;
- }
-
- // FIRST LOOP: Perform a layout, if needed.
- if (repeats < LAYOUT_REPEAT_THRESHOLD) {
- performLayoutLockedInner(displayContent, repeats == 1,
- false /*updateInputWindows*/);
- } else {
- Slog.w(TAG, "Layout repeat skipped after too many iterations");
- }
-
- // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
- // it is animating.
- displayContent.pendingLayoutChanges = 0;
-
- if (isDefaultDisplay) {
- mPolicy.beginPostLayoutPolicyLw(dw, dh);
- for (i = windows.size() - 1; i >= 0; i--) {
- WindowState w = windows.get(i);
- if (w.mHasSurface) {
- mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs, w.mAttachedWindow);
- }
- }
- displayContent.pendingLayoutChanges |= mPolicy.finishPostLayoutPolicyLw();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats(
- "after finishPostLayoutPolicyLw", displayContent.pendingLayoutChanges);
- }
- } while (displayContent.pendingLayoutChanges != 0);
-
- mInnerFields.mObscured = false;
- mInnerFields.mSyswin = false;
- displayContent.resetDimming();
-
- // Only used if default window
- final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
-
- final int N = windows.size();
- for (i=N-1; i>=0; i--) {
- WindowState w = windows.get(i);
- final Task task = w.getTask();
- if (task == null && w.getAttrs().type != TYPE_PRIVATE_PRESENTATION) {
- continue;
- }
-
- final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
-
- // Update effect.
- w.mObscured = mInnerFields.mObscured;
- if (!mInnerFields.mObscured) {
- handleNotObscuredLocked(w, innerDw, innerDh);
- }
-
- if (task != null && !task.getContinueDimming()) {
- handleFlagDimBehind(w);
- }
-
- if (isDefaultDisplay && obscuredChanged
- && mWallpaperControllerLocked.isWallpaperTarget(w) && w.isVisibleLw()) {
- // This is the wallpaper target and its obscured state
- // changed... make sure the current wallaper's visibility
- // has been updated accordingly.
- mWallpaperControllerLocked.updateWallpaperVisibility();
- }
-
- final WindowStateAnimator winAnimator = w.mWinAnimator;
-
- // If the window has moved due to its containing content frame changing, then
- // notify the listeners and optionally animate it.
- if (w.hasMoved()) {
- // Frame has moved, containing content frame has also moved, and we're not
- // currently animating... let's do something.
- final int left = w.mFrame.left;
- final int top = w.mFrame.top;
- if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0) {
- Animation a = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.window_move_from_decor);
- winAnimator.setAnimation(a);
- winAnimator.mAnimDw = w.mLastFrame.left - left;
- winAnimator.mAnimDh = w.mLastFrame.top - top;
- winAnimator.mAnimateMove = true;
- winAnimator.mAnimatingMove = true;
- }
-
- //TODO (multidisplay): Accessibility supported only for the default display.
- if (mAccessibilityController != null
- && displayId == Display.DEFAULT_DISPLAY) {
- mAccessibilityController.onSomeWindowResizedOrMovedLocked();
- }
-
- try {
- w.mClient.moved(left, top);
- } catch (RemoteException e) {
- }
- }
-
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
- w.mContentChanged = false;
-
- // Moved from updateWindowsAndWallpaperLocked().
- if (w.mHasSurface) {
- // Take care of the window being ready to display.
- final boolean committed =
- winAnimator.commitFinishDrawingLocked();
- if (isDefaultDisplay && committed) {
- if (w.mAttrs.type == TYPE_DREAM) {
- // HACK: When a dream is shown, it may at that
- // point hide the lock screen. So we need to
- // redo the layout to let the phone window manager
- // make this happen.
- displayContent.pendingLayoutChanges |=
- WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) {
- debugLayoutRepeats(
- "dream and commitFinishDrawingLocked true",
- displayContent.pendingLayoutChanges);
- }
- }
- if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
- "First draw done in potential wallpaper target " + w);
- mInnerFields.mWallpaperMayChange = true;
- displayContent.pendingLayoutChanges |=
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (DEBUG_LAYOUT_REPEATS) {
- debugLayoutRepeats(
- "wallpaper and commitFinishDrawingLocked true",
- displayContent.pendingLayoutChanges);
- }
- }
- }
-
- winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
- }
-
- final AppWindowToken atoken = w.mAppToken;
- if (DEBUG_STARTING_WINDOW && atoken != null
- && w == atoken.startingWindow) {
- Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen="
- + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
- }
- if (atoken != null
- && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
- if (atoken.lastTransactionSequence != mTransactionSequence) {
- atoken.lastTransactionSequence = mTransactionSequence;
- atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
- atoken.startingDisplayed = false;
- }
- if ((w.isOnScreenIgnoringKeyguard()
- || winAnimator.mAttrType == TYPE_BASE_APPLICATION)
- && !w.mExiting && !w.mDestroying) {
- if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
- Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
- + ", isAnimating=" + winAnimator.isAnimating());
- if (!w.isDrawnLw()) {
- Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceControl
- + " pv=" + w.mPolicyVisibility
- + " mDrawState=" + winAnimator.drawStateToString()
- + " ah=" + w.mAttachedHidden
- + " th=" + atoken.hiddenRequested
- + " a=" + winAnimator.mAnimating);
- }
- }
- if (w != atoken.startingWindow) {
- if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
- atoken.numInterestingWindows++;
- if (w.isDrawnLw()) {
- atoken.numDrawnWindows++;
- if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG,
- "tokenMayBeDrawn: " + atoken
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
- + " mAppFreezing=" + w.mAppFreezing);
- updateAllDrawn = true;
- }
- }
- } else if (w.isDrawnLw()) {
- atoken.startingDisplayed = true;
- }
- }
- }
-
- if (isDefaultDisplay && someoneLosingFocus && (w == mCurrentFocus)
- && w.isDisplayedLw()) {
- focusDisplayed = true;
- }
-
- updateResizingWindows(w);
- }
-
- mDisplayManagerInternal.setDisplayProperties(displayId,
- mInnerFields.mDisplayHasContent, mInnerFields.mPreferredRefreshRate,
- mInnerFields.mPreferredModeId,
- true /* inTraversal, must call performTraversalInTrans... below */);
-
- getDisplayContentLocked(displayId).stopDimmingIfNeeded();
-
- if (updateAllDrawn) {
- updateAllDrawnLocked(displayContent);
- }
- }
-
- if (focusDisplayed) {
- mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
- }
-
- // Give the display manager a chance to adjust properties
- // like display rotation if it needs to.
- mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
-
- } catch (RuntimeException e) {
- Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
- } finally {
- SurfaceControl.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
- }
-
- final WindowList defaultWindows = defaultDisplay.getWindowList();
-
- // If we are ready to perform an app transition, check through
- // all of the app tokens to be shown and see if they are ready
- // to go.
- if (mAppTransition.isReady()) {
- defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
- defaultDisplay.pendingLayoutChanges);
- }
-
- if (!mAnimator.mAppWindowAnimating && mAppTransition.isRunning()) {
- // We have finished the animation of an app transition. To do
- // this, we have delayed a lot of operations like showing and
- // hiding apps, moving apps in Z-order, etc. The app token list
- // reflects the correct Z-order, but the window list may now
- // be out of sync with it. So here we will just rebuild the
- // entire app window list. Fun!
- defaultDisplay.pendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock",
- defaultDisplay.pendingLayoutChanges);
- }
-
- if (mInnerFields.mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
- && !mAppTransition.isReady()) {
- // At this point, there was a window with a wallpaper that
- // was force hiding other windows behind it, but now it
- // is going away. This may be simple -- just animate
- // away the wallpaper and its window -- or it may be
- // hard -- the wallpaper now needs to be shown behind
- // something that was hidden.
- defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked",
- defaultDisplay.pendingLayoutChanges);
- }
- mInnerFields.mWallpaperForceHidingChanged = false;
-
- if (mInnerFields.mWallpaperMayChange) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
- defaultDisplay.pendingLayoutChanges |=
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("WallpaperMayChange",
- defaultDisplay.pendingLayoutChanges);
- }
-
- if (mFocusMayChange) {
- mFocusMayChange = false;
- if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
- false /*updateInputWindows*/)) {
- updateInputWindowsNeeded = true;
- defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- }
- }
-
- if (needsLayout()) {
- defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded",
- defaultDisplay.pendingLayoutChanges);
- }
-
- for (i = mResizingWindows.size() - 1; i >= 0; i--) {
- WindowState win = mResizingWindows.get(i);
- if (win.mAppFreezing) {
- // Don't remove this window until rotation has completed.
- continue;
- }
- win.reportResized();
- mResizingWindows.remove(i);
- }
-
- if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG,
- "With display frozen, orientationChangeComplete="
- + mInnerFields.mOrientationChangeComplete);
- if (mInnerFields.mOrientationChangeComplete) {
- if (mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
- mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
- mLastFinishedFreezeSource = mInnerFields.mLastWindowFreezeSource;
- mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
- }
- stopFreezingDisplayLocked();
- }
-
- // Destroy the surface of any windows that are no longer visible.
- boolean wallpaperDestroyed = false;
- i = mDestroySurface.size();
- if (i > 0) {
- do {
- i--;
- WindowState win = mDestroySurface.get(i);
- win.mDestroying = false;
- if (mInputMethodWindow == win) {
- mInputMethodWindow = null;
- }
- if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
- wallpaperDestroyed = true;
- }
- win.mWinAnimator.destroySurfaceLocked();
- } while (i > 0);
- mDestroySurface.clear();
- }
-
- // Time to remove any exiting tokens?
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
- ArrayList<WindowToken> exitingTokens = displayContent.mExitingTokens;
- for (i = exitingTokens.size() - 1; i >= 0; i--) {
- WindowToken token = exitingTokens.get(i);
- if (!token.hasVisible) {
- exitingTokens.remove(i);
- if (token.windowType == TYPE_WALLPAPER) {
- mWallpaperControllerLocked.removeWallpaperToken(token);
- }
- }
- }
- }
-
- // Time to remove any exiting applications?
- for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; --stackNdx) {
- // Initialize state of exiting applications.
- final AppTokenList exitingAppTokens =
- mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
- for (i = exitingAppTokens.size() - 1; i >= 0; i--) {
- AppWindowToken token = exitingAppTokens.get(i);
- if (!token.hasVisible && !mClosingApps.contains(token) &&
- (!token.mIsExiting || token.allAppWindows.isEmpty())) {
- // Make sure there is no animation running on this token,
- // so any windows associated with it will be removed as
- // soon as their animations are complete
- token.mAppAnimator.clearAnimation();
- token.mAppAnimator.animating = false;
- if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
- "performLayout: App token exiting now removed" + token);
- token.removeAppFromTaskLocked();
- }
- }
- }
-
- if (wallpaperDestroyed) {
- defaultDisplay.pendingLayoutChanges |=
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- defaultDisplay.layoutNeeded = true;
- }
-
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
- if (displayContent.pendingLayoutChanges != 0) {
- displayContent.layoutNeeded = true;
- }
- }
-
- // Finally update all input windows now that the window changes have stabilized.
- mInputMonitor.updateInputWindowsLw(true /*force*/);
-
- setHoldScreenLocked(mInnerFields.mHoldScreen);
- if (!mDisplayFrozen) {
- if (mInnerFields.mScreenBrightness < 0 || mInnerFields.mScreenBrightness > 1.0f) {
- mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(-1);
- } else {
- mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
- toBrightnessOverride(mInnerFields.mScreenBrightness));
- }
- if (mInnerFields.mButtonBrightness < 0 || mInnerFields.mButtonBrightness > 1.0f) {
- mPowerManagerInternal.setButtonBrightnessOverrideFromWindowManager(-1);
- } else {
- mPowerManagerInternal.setButtonBrightnessOverrideFromWindowManager(
- toBrightnessOverride(mInnerFields.mButtonBrightness));
- }
- mPowerManagerInternal.setUserActivityTimeoutOverrideFromWindowManager(
- mInnerFields.mUserActivityTimeout);
- }
-
- if (mTurnOnScreen) {
- if (mAllowTheaterModeWakeFromLayout
- || Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.THEATER_MODE_ON, 0) == 0) {
- if (DEBUG_VISIBILITY || DEBUG_POWER) {
- Slog.v(TAG, "Turning screen on after layout!");
- }
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.wm:TURN_ON");
- }
- mTurnOnScreen = false;
- }
-
- if (mInnerFields.mUpdateRotation) {
- if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
- if (updateRotationUncheckedLocked(false)) {
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
- } else {
- mInnerFields.mUpdateRotation = false;
- }
- }
-
- if (mWaitingForDrawnCallback != null ||
- (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded &&
- !mInnerFields.mUpdateRotation)) {
- checkDrawnWindowsLocked();
- }
-
- final int N = mPendingRemove.size();
- if (N > 0) {
- if (mPendingRemoveTmp.length < N) {
- mPendingRemoveTmp = new WindowState[N+10];
- }
- mPendingRemove.toArray(mPendingRemoveTmp);
- mPendingRemove.clear();
- DisplayContentList displayList = new DisplayContentList();
- for (i = 0; i < N; i++) {
- WindowState w = mPendingRemoveTmp[i];
- removeWindowInnerLocked(w);
- final DisplayContent displayContent = w.getDisplayContent();
- if (displayContent != null && !displayList.contains(displayContent)) {
- displayList.add(displayContent);
- }
- }
-
- for (DisplayContent displayContent : displayList) {
- assignLayersLocked(displayContent.getWindowList());
- displayContent.layoutNeeded = true;
- }
- }
-
- // Remove all deferred displays stacks, tasks, and activities.
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- mDisplayContents.valueAt(displayNdx).checkForDeferredActions();
- }
-
- if (updateInputWindowsNeeded) {
- mInputMonitor.updateInputWindowsLw(false /*force*/);
- }
- setFocusTaskRegion();
-
- // Check to see if we are now in a state where the screen should
- // be enabled, because the window obscured flags have changed.
- enableScreenIfNeededLocked();
-
- scheduleAnimationLocked();
-
- if (DEBUG_WINDOW_TRACE) {
- Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: animating="
- + mAnimator.mAnimating);
- }
- }
-
- private int toBrightnessOverride(float value) {
- return (int)(value * PowerManager.BRIGHTNESS_ON);
- }
-
void checkDrawnWindowsLocked() {
if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
return;
@@ -9807,14 +8551,7 @@
void requestTraversal() {
synchronized (mWindowMap) {
- requestTraversalLocked();
- }
- }
-
- void requestTraversalLocked() {
- if (!mTraversalScheduled) {
- mTraversalScheduled = true;
- mH.sendEmptyMessage(H.DO_TRAVERSAL);
+ mWindowPlacerLocked.requestTraversal();
}
}
@@ -9826,7 +8563,7 @@
}
}
- private boolean needsLayout() {
+ boolean needsLayout() {
final int numDisplays = mDisplayContents.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
@@ -9837,41 +8574,6 @@
return false;
}
- boolean copyAnimToLayoutParamsLocked() {
- boolean doRequest = false;
-
- final int bulkUpdateParams = mAnimator.mBulkUpdateParams;
- if ((bulkUpdateParams & LayoutFields.SET_UPDATE_ROTATION) != 0) {
- mInnerFields.mUpdateRotation = true;
- doRequest = true;
- }
- if ((bulkUpdateParams & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) {
- mInnerFields.mWallpaperMayChange = true;
- doRequest = true;
- }
- if ((bulkUpdateParams & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) {
- mInnerFields.mWallpaperForceHidingChanged = true;
- doRequest = true;
- }
- if ((bulkUpdateParams & LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
- mInnerFields.mOrientationChangeComplete = false;
- } else {
- mInnerFields.mOrientationChangeComplete = true;
- mInnerFields.mLastWindowFreezeSource = mAnimator.mLastWindowFreezeSource;
- if (mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
- doRequest = true;
- }
- }
- if ((bulkUpdateParams & LayoutFields.SET_TURN_ON_SCREEN) != 0) {
- mTurnOnScreen = true;
- }
- if ((bulkUpdateParams & LayoutFields.SET_WALLPAPER_ACTION_PENDING) != 0) {
- mInnerFields.mWallpaperActionPending = true;
- }
-
- return doRequest;
- }
-
/** If a window that has an animation specifying a colored background and the current wallpaper
* is visible, then the color goes *below* the wallpaper so we don't cause the wallpaper to
* suddenly disappear. */
@@ -9995,7 +8697,7 @@
return leakedSurface || killedApps;
}
- private boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
+ boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
WindowState newFocus = computeFocusedWindowLocked();
if (mCurrentFocus != newFocus) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
@@ -10024,7 +8726,8 @@
if (imWindowChanged && oldFocus != mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
+ mWindowPlacerLocked.performLayoutLockedInner(displayContent, true /*initial*/,
+ updateInputWindows);
focusChanged &= ~WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
@@ -10037,7 +8740,8 @@
// The change in focus caused us to need to do a layout. Okay.
displayContent.layoutNeeded = true;
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
+ mWindowPlacerLocked.performLayoutLockedInner(displayContent, true /*initial*/,
+ updateInputWindows);
}
}
@@ -10065,7 +8769,7 @@
return null;
}
- private WindowState findFocusedWindowLocked(DisplayContent displayContent) {
+ WindowState findFocusedWindowLocked(DisplayContent displayContent) {
final WindowList windows = displayContent.getWindowList();
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState win = windows.get(i);
@@ -10189,7 +8893,7 @@
}
}
- private void stopFreezingDisplayLocked() {
+ void stopFreezingDisplayLocked() {
if (!mDisplayFrozen) {
return;
}
@@ -10396,7 +9100,7 @@
synchronized (mWindowMap) {
int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
updateStatusBarVisibilityLocked(visibility);
- performLayoutAndPlaceSurfacesLocked();
+ mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -10711,6 +9415,7 @@
if (mInputMethodWindow != null) {
pw.print(" mInputMethodWindow="); pw.println(mInputMethodWindow);
}
+ mWindowPlacerLocked.dump(pw, " ");
mWallpaperControllerLocked.dump(pw, " ");
if (mInputMethodAnimLayerAdjustment != 0 ||
mWallpaperControllerLocked.getAnimLayerAdjustment() != 0) {
@@ -10746,8 +9451,7 @@
pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
pw.print(" animator="); pw.println(mAnimatorDurationScaleSetting);
- pw.print(" mTraversalScheduled="); pw.println(mTraversalScheduled);
- pw.print(" mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
+ pw.print(" mSkipAppTransitionAnimation=");pw.println(mSkipAppTransitionAnimation);
pw.println(" mLayoutToAnim:");
mAppTransition.dump(pw, " ");
}
@@ -11010,13 +9714,6 @@
synchronized (mWindowMap) { }
}
- void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
- if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
- Slog.v(TAG, "Layouts looping: " + msg + ", mPendingLayoutChanges = 0x" +
- Integer.toHexString(pendingLayoutChanges));
- }
- }
-
private DisplayContent newDisplayContentLocked(final Display display) {
DisplayContent displayContent = new DisplayContent(display, this);
final int displayId = display.getDisplayId();
@@ -11112,7 +9809,7 @@
createDisplayContentLocked(display);
displayReady(displayId);
}
- requestTraversalLocked();
+ mWindowPlacerLocked.requestTraversal();
}
}
@@ -11135,7 +9832,7 @@
}
}
mAnimator.removeDisplayLocked(displayId);
- requestTraversalLocked();
+ mWindowPlacerLocked.requestTraversal();
}
public void onDisplayChanged(int displayId) {
@@ -11147,7 +9844,7 @@
if (displayContent != null) {
displayContent.updateDisplayInfo();
}
- requestTraversalLocked();
+ mWindowPlacerLocked.requestTraversal();
}
@Override
@@ -11155,6 +9852,22 @@
return mWindowMap;
}
+ public void setReplacingWindow(IBinder token, boolean animate) {
+ synchronized (mWindowMap) {
+ AppWindowToken appWindowToken = findAppWindowToken(token);
+ if (appWindowToken == null) {
+ Slog.w(TAG, "Attempted to set replacing window on non-existing app token " + token);
+ return;
+ }
+ appWindowToken.mReplacingWindow = true;
+ appWindowToken.mAnimateReplacingWindow = animate;
+ }
+ }
+
+ 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() {
@@ -11282,7 +9995,7 @@
}
}
}
- requestTraversalLocked();
+ mWindowPlacerLocked.requestTraversal();
}
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
if (mWaitingForDrawn.isEmpty()) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 761e5eb..94bc8d75 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
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;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -80,12 +81,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;
@@ -544,11 +547,19 @@
@Override
public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
Rect osf) {
+ if (mAppToken != null && mAppToken.mReplacingWindow
+ && (mExiting || !mAppToken.mReplacingRemoveRequested)) {
+ // This window is being replaced and either already got information that it's being
+ // removed or we are still waiting for some information. Because of this we don't
+ // want to apply any more changes to it, so it remains in this state until new window
+ // appears.
+ return;
+ }
mHaveFrame = true;
final Task task = mAppToken != null ? getTask() : null;
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;
@@ -680,8 +691,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,
@@ -888,6 +902,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) {
@@ -916,24 +935,37 @@
}
/**
- * 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) {
+ final boolean useStackBounds = mAppToken != null && mAppToken.mCropWindowsToStack;
+ boolean isFreeform = false;
+ bounds.setEmpty();
+ if (useStackBounds) {
+ final TaskStack stack = getStack();
+ if (stack != null) {
+ stack.getBounds(bounds);
+ isFreeform = stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
+ } else {
+ final Task task = getTask();
+ if (task != null) {
+ task.getBounds(bounds);
+ isFreeform = task.inFreeformWorkspace();
+ }
+ }
+ if (bounds.isEmpty()) {
+ bounds.set(mFrame);
return;
}
- bounds.set(mFrame);
+ if (forTouch && isFreeform) {
+ final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+ final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
+ bounds.inset(-delta, -delta);
+ }
}
public long getInputDispatchingTimeoutNanos() {
@@ -1255,6 +1287,35 @@
mInputWindowHandle.inputChannel = null;
}
+ void handleFlagDimBehind() {
+ if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && isDisplayedLw() && !mExiting) {
+ final Task task = getTask();
+ if (task == null) {
+ return;
+ }
+ task.setContinueDimming();
+ if (!task.isDimming(mWinAnimator)) {
+ if (WindowManagerService.localLOGV) Slog.v(TAG, "Win " + this + " start dimming.");
+ task.startDimmingIfNeeded(mWinAnimator);
+ }
+ }
+ }
+
+ void maybeRemoveReplacedWindow() {
+ AppWindowToken token = mAppToken;
+ if (token != null && token.mReplacingWindow) {
+ token.mReplacingWindow = false;
+ token.mAnimateReplacingWindow = false;
+ token.mReplacingRemoveRequested = false;
+ for (int i = token.allAppWindows.size() - 1; i >= 0; i--) {
+ WindowState win = token.allAppWindows.get(i);
+ if (win.mExiting) {
+ mService.removeWindowInnerLocked(win);
+ }
+ }
+ }
+ }
+
private class DeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
@@ -1602,7 +1663,7 @@
Slog.w(TAG, "Failed to report 'resized' to the client of " + this
+ ", removing this window.");
mService.mPendingRemove.add(this);
- mService.requestTraversalLocked();
+ mService.mWindowPlacerLocked.requestTraversal();
}
}
@@ -1630,16 +1691,13 @@
}
boolean inFreeformWorkspace() {
- final Task task = getTask();
- return task != null && task.mStack != null &&
- task.mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+ final Task task = mAppToken != null ? getTask() : null;
+ 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 isDragResizing() {
+ final Task task = mAppToken != null ? getTask() : null;
+ return mService.mTaskPositioner != null && mService.mTaskPositioner.isTaskResizing(task);
}
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 109e627..2fe7fd6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -27,8 +27,8 @@
import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerService.localLOGV;
-import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.WindowManagerService.LayoutFields.SET_TURN_ON_SCREEN;
+import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
+import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
import android.content.Context;
import android.graphics.Matrix;
@@ -115,6 +115,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 +169,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";
@@ -424,8 +430,9 @@
finishExit();
final int displayId = mWin.getDisplayId();
mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats(
- "WindowStateAnimator", mAnimator.getPendingLayoutChanges(displayId));
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS)
+ mService.mWindowPlacerLocked.debugLayoutRepeats(
+ "WindowStateAnimator", mAnimator.getPendingLayoutChanges(displayId));
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
@@ -797,8 +804,17 @@
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()) {
+ width = displayInfo.logicalWidth;
+ height = displayInfo.logicalHeight;
+ } else {
+ width = w.mCompatFrame.width();
+ height = w.mCompatFrame.height();
+ }
}
// Something is wrong and SurfaceFlinger will not like this,
@@ -1184,7 +1200,7 @@
+ " screen=" + (screenAnimation ?
screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
return;
- } else if (mIsWallpaper && mService.mInnerFields.mWallpaperActionPending) {
+ } else if (mIsWallpaper && mService.mWindowPlacerLocked.mWallpaperActionPending) {
return;
}
@@ -1308,9 +1324,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;
@@ -1330,12 +1352,20 @@
// so we need to translate to match the actual surface coordinates.
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
+ 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);
@@ -1346,6 +1376,26 @@
}
}
+ 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;
@@ -1357,8 +1407,17 @@
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()) {
+ width = displayInfo.logicalWidth;
+ height = displayInfo.logicalHeight;
+ } else {
+ width = w.mCompatFrame.width();
+ height = w.mCompatFrame.height();
+ }
}
// Something is wrong and SurfaceFlinger will not like this,
@@ -1445,7 +1504,7 @@
updateSurfaceWindowCrop(recoveringMemory);
}
- public void prepareSurfaceLocked(final boolean recoveringMemory) {
+ void prepareSurfaceLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
if (mSurfaceControl == null) {
if (w.mOrientationChanging) {
@@ -1500,6 +1559,7 @@
w.mLastHScale = w.mHScale;
w.mLastVScale = w.mVScale;
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+ "control=" + mSurfaceControl +
"alpha=" + mShownAlpha + " layer=" + mAnimLayer
+ " matrix=[" + mDsDx + "*" + w.mHScale
+ "," + mDtDx + "*" + w.mVScale
@@ -1515,7 +1575,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
@@ -1764,6 +1824,8 @@
mWin.mAppToken.updateReportedVisibilityLocked();
}
+ mWin.maybeRemoveReplacedWindow();
+
return true;
}
@@ -1778,17 +1840,14 @@
*
* @return Returns true if the surface was successfully shown.
*/
- boolean showSurfaceRobustlyLocked() {
+ private boolean showSurfaceRobustlyLocked() {
try {
- if (mSurfaceControl != null) {
- mSurfaceShown = true;
- mSurfaceControl.show();
- if (mWin.mTurnOnScreen) {
- if (DEBUG_VISIBILITY) Slog.v(TAG,
- "Show surface turning screen on: " + mWin);
- mWin.mTurnOnScreen = false;
- mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
- }
+ mSurfaceShown = true;
+ mSurfaceControl.show();
+ if (mWin.mTurnOnScreen) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "Show surface turning screen on: " + mWin);
+ mWin.mTurnOnScreen = false;
+ mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
}
return true;
} catch (RuntimeException e) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
new file mode 100644
index 0000000..86da94f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -0,0 +1,1526 @@
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.WindowManagerService.DEBUG;
+import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
+import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerService.DEBUG_POWER;
+import static com.android.server.wm.WindowManagerService.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerService.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.DEBUG_WALLPAPER_LIGHT;
+import static com.android.server.wm.WindowManagerService.DEBUG_WINDOW_TRACE;
+import static com.android.server.wm.WindowManagerService.H.*;
+import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
+import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
+import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.TAG;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Debug;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Positions windows and their surfaces.
+ *
+ * It sets positions of windows by calculating their frames and then applies this by positioning
+ * surfaces according to these frames. Z layer is still assigned withing WindowManagerService.
+ */
+class WindowSurfacePlacer {
+ private final WindowManagerService mService;
+ private final WallpaperController mWallpaperControllerLocked;
+
+ private boolean mInLayout = false;
+
+ /** Only do a maximum of 6 repeated layouts. After that quit */
+ private int mLayoutRepeatCount;
+
+ static final int SET_UPDATE_ROTATION = 1 << 0;
+ static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
+ static final int SET_FORCE_HIDING_CHANGED = 1 << 2;
+ static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3;
+ static final int SET_TURN_ON_SCREEN = 1 << 4;
+ static final int SET_WALLPAPER_ACTION_PENDING = 1 << 5;
+
+ boolean mWallpaperMayChange = false;
+ boolean mOrientationChangeComplete = true;
+ boolean mWallpaperActionPending = false;
+
+ private boolean mWallpaperForceHidingChanged = false;
+ private Object mLastWindowFreezeSource = null;
+ private Session mHoldScreen = null;
+ private boolean mObscured = false;
+ private boolean mSyswin = false;
+ private float mScreenBrightness = -1;
+ private float mButtonBrightness = -1;
+ private long mUserActivityTimeout = -1;
+ private boolean mUpdateRotation = false;
+ private final Rect mTmpStartRect = new Rect();
+
+ // Set to true when the display contains content to show the user.
+ // When false, the display manager may choose to mirror or blank the display.
+ private boolean mDisplayHasContent = false;
+
+ // Only set while traversing the default display based on its content.
+ // Affects the behavior of mirroring on secondary displays.
+ private boolean mObscureApplicationContentOnSecondaryDisplays = false;
+
+ private float mPreferredRefreshRate = 0;
+
+ private int mPreferredModeId = 0;
+
+ private boolean mTraversalScheduled;
+
+ public WindowSurfacePlacer(WindowManagerService service) {
+ mService = service;
+ mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
+ }
+
+ final void performSurfacePlacement() {
+ int loopCount = 6;
+ do {
+ mTraversalScheduled = false;
+ performSurfacePlacementLoop();
+ mService.mH.removeMessages(DO_TRAVERSAL);
+ loopCount--;
+ } while (mTraversalScheduled && loopCount > 0);
+ mWallpaperActionPending = false;
+ }
+
+ private void performSurfacePlacementLoop() {
+ if (mInLayout) {
+ if (DEBUG) {
+ throw new RuntimeException("Recursive call!");
+ }
+ Slog.w(TAG,
+ "performLayoutAndPlaceSurfacesLocked called while in layout. Callers="
+ + Debug.getCallers(3));
+ return;
+ }
+
+ if (mService.mWaitingForConfig) {
+ // Our configuration has changed (most likely rotation), but we
+ // don't yet have the complete configuration to report to
+ // applications. Don't do any window layout until we have it.
+ return;
+ }
+
+ if (!mService.mDisplayReady) {
+ // Not yet initialized, nothing to do.
+ return;
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
+ mInLayout = true;
+
+ boolean recoveringMemory = false;
+ if (!mService.mForceRemoves.isEmpty()) {
+ recoveringMemory = true;
+ // Wait a little bit for things to settle down, and off we go.
+ while (!mService.mForceRemoves.isEmpty()) {
+ WindowState ws = mService.mForceRemoves.remove(0);
+ Slog.i(TAG, "Force removing: " + ws);
+ mService.removeWindowInnerLocked(ws);
+ }
+ Slog.w(TAG,
+ "Due to memory failure, waiting a bit for next layout");
+ Object tmp = new Object();
+ synchronized (tmp) {
+ try {
+ tmp.wait(250);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ try {
+ performSurfacePlacementInner(recoveringMemory);
+
+ mInLayout = false;
+
+ if (mService.needsLayout()) {
+ if (++mLayoutRepeatCount < 6) {
+ requestTraversal();
+ } else {
+ Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
+ mLayoutRepeatCount = 0;
+ }
+ } else {
+ mLayoutRepeatCount = 0;
+ }
+
+ if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
+ mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
+ mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
+ }
+ } catch (RuntimeException e) {
+ mInLayout = false;
+ Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
+ }
+
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
+ if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
+ Slog.v(TAG, "Layouts looping: " + msg +
+ ", mPendingLayoutChanges = 0x" + Integer.toHexString(pendingLayoutChanges));
+ }
+ }
+
+ // "Something has changed! Let's make it correct now."
+ private void performSurfacePlacementInner(boolean recoveringMemory) {
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.v(TAG,
+ "performSurfacePlacementInner: entry. Called by "
+ + Debug.getCallers(3));
+ }
+
+ int i;
+ boolean updateInputWindowsNeeded = false;
+
+ if (mService.mFocusMayChange) {
+ mService.mFocusMayChange = false;
+ updateInputWindowsNeeded = mService.updateFocusedWindowLocked(
+ UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
+ }
+
+ // Initialize state of exiting tokens.
+ final int numDisplays = mService.mDisplayContents.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
+ for (i=displayContent.mExitingTokens.size()-1; i>=0; i--) {
+ displayContent.mExitingTokens.get(i).hasVisible = false;
+ }
+ }
+
+ for (int stackNdx = mService.mStackIdToStack.size() - 1; stackNdx >= 0; --stackNdx) {
+ // Initialize state of exiting applications.
+ final AppTokenList exitingAppTokens =
+ mService.mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
+ for (int tokenNdx = exitingAppTokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
+ exitingAppTokens.get(tokenNdx).hasVisible = false;
+ }
+ }
+
+ mHoldScreen = null;
+ mScreenBrightness = -1;
+ mButtonBrightness = -1;
+ mUserActivityTimeout = -1;
+ mObscureApplicationContentOnSecondaryDisplays = false;
+
+ mService.mTransactionSequence++;
+
+ final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
+ final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
+ final int defaultDw = defaultInfo.logicalWidth;
+ final int defaultDh = defaultInfo.logicalHeight;
+
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
+ SurfaceControl.openTransaction();
+ try {
+ applySurfaceChangesTransaction(recoveringMemory, numDisplays, defaultDw, defaultDh);
+ } catch (RuntimeException e) {
+ Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
+ }
+
+ final WindowList defaultWindows = defaultDisplay.getWindowList();
+
+ // If we are ready to perform an app transition, check through
+ // all of the app tokens to be shown and see if they are ready
+ // to go.
+ if (mService.mAppTransition.isReady()) {
+ defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);
+ if (DEBUG_LAYOUT_REPEATS)
+ debugLayoutRepeats("after handleAppTransitionReadyLocked",
+ defaultDisplay.pendingLayoutChanges);
+ }
+
+ if (!mService.mAnimator.mAppWindowAnimating && mService.mAppTransition.isRunning()) {
+ // We have finished the animation of an app transition. To do
+ // this, we have delayed a lot of operations like showing and
+ // hiding apps, moving apps in Z-order, etc. The app token list
+ // reflects the correct Z-order, but the window list may now
+ // be out of sync with it. So here we will just rebuild the
+ // entire app window list. Fun!
+ defaultDisplay.pendingLayoutChanges |=
+ mService.handleAnimatingStoppedAndTransitionLocked();
+ if (DEBUG_LAYOUT_REPEATS)
+ debugLayoutRepeats("after handleAnimStopAndXitionLock",
+ defaultDisplay.pendingLayoutChanges);
+ }
+
+ if (mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
+ && !mService.mAppTransition.isReady()) {
+ // At this point, there was a window with a wallpaper that
+ // was force hiding other windows behind it, but now it
+ // is going away. This may be simple -- just animate
+ // away the wallpaper and its window -- or it may be
+ // hard -- the wallpaper now needs to be shown behind
+ // something that was hidden.
+ defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
+ if (DEBUG_LAYOUT_REPEATS)
+ debugLayoutRepeats("after animateAwayWallpaperLocked",
+ defaultDisplay.pendingLayoutChanges);
+ }
+ mWallpaperForceHidingChanged = false;
+
+ if (mWallpaperMayChange) {
+ if (DEBUG_WALLPAPER_LIGHT)
+ Slog.v(TAG, "Wallpaper may change! Adjusting");
+ defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("WallpaperMayChange",
+ defaultDisplay.pendingLayoutChanges);
+ }
+
+ if (mService.mFocusMayChange) {
+ mService.mFocusMayChange = false;
+ if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+ false /*updateInputWindows*/)) {
+ updateInputWindowsNeeded = true;
+ defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
+ }
+ }
+
+ if (mService.needsLayout()) {
+ defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded",
+ defaultDisplay.pendingLayoutChanges);
+ }
+
+ for (i = mService.mResizingWindows.size() - 1; i >= 0; i--) {
+ WindowState win = mService.mResizingWindows.get(i);
+ if (win.mAppFreezing) {
+ // Don't remove this window until rotation has completed.
+ continue;
+ }
+ win.reportResized();
+ mService.mResizingWindows.remove(i);
+ }
+
+ if (DEBUG_ORIENTATION && mService.mDisplayFrozen)
+ Slog.v(TAG,
+ "With display frozen, orientationChangeComplete="
+ + mOrientationChangeComplete);
+ if (mOrientationChangeComplete) {
+ if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
+ mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
+ mService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
+ mService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
+ }
+ mService.stopFreezingDisplayLocked();
+ }
+
+ // Destroy the surface of any windows that are no longer visible.
+ boolean wallpaperDestroyed = false;
+ i = mService.mDestroySurface.size();
+ if (i > 0) {
+ do {
+ i--;
+ WindowState win = mService.mDestroySurface.get(i);
+ win.mDestroying = false;
+ if (mService.mInputMethodWindow == win) {
+ mService.mInputMethodWindow = null;
+ }
+ if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
+ wallpaperDestroyed = true;
+ }
+ win.mWinAnimator.destroySurfaceLocked();
+ } while (i > 0);
+ mService.mDestroySurface.clear();
+ }
+
+ // Time to remove any exiting tokens?
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
+ ArrayList<WindowToken> exitingTokens = displayContent.mExitingTokens;
+ for (i = exitingTokens.size() - 1; i >= 0; i--) {
+ WindowToken token = exitingTokens.get(i);
+ if (!token.hasVisible) {
+ exitingTokens.remove(i);
+ if (token.windowType == TYPE_WALLPAPER) {
+ mWallpaperControllerLocked.removeWallpaperToken(token);
+ }
+ }
+ }
+ }
+
+ // Time to remove any exiting applications?
+ for (int stackNdx = mService.mStackIdToStack.size() - 1; stackNdx >= 0; --stackNdx) {
+ // Initialize state of exiting applications.
+ final AppTokenList exitingAppTokens =
+ mService.mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
+ for (i = exitingAppTokens.size() - 1; i >= 0; i--) {
+ AppWindowToken token = exitingAppTokens.get(i);
+ if (!token.hasVisible && !mService.mClosingApps.contains(token) &&
+ (!token.mIsExiting || token.allAppWindows.isEmpty())) {
+ // Make sure there is no animation running on this token,
+ // so any windows associated with it will be removed as
+ // soon as their animations are complete
+ token.mAppAnimator.clearAnimation();
+ token.mAppAnimator.animating = false;
+ if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT)
+ Slog.v(TAG,
+ "performLayout: App token exiting now removed" + token);
+ token.removeAppFromTaskLocked();
+ }
+ }
+ }
+
+ if (wallpaperDestroyed) {
+ defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ defaultDisplay.layoutNeeded = true;
+ }
+
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
+ if (displayContent.pendingLayoutChanges != 0) {
+ displayContent.layoutNeeded = true;
+ }
+ }
+
+ // Finally update all input windows now that the window changes have stabilized.
+ mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+
+ mService.setHoldScreenLocked(mHoldScreen);
+ if (!mService.mDisplayFrozen) {
+ if (mScreenBrightness < 0 || mScreenBrightness > 1.0f) {
+ mService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(-1);
+ } else {
+ mService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
+ toBrightnessOverride(mScreenBrightness));
+ }
+ if (mButtonBrightness < 0
+ || mButtonBrightness > 1.0f) {
+ mService.mPowerManagerInternal.setButtonBrightnessOverrideFromWindowManager(-1);
+ } else {
+ mService.mPowerManagerInternal.setButtonBrightnessOverrideFromWindowManager(
+ toBrightnessOverride(mButtonBrightness));
+ }
+ mService.mPowerManagerInternal.setUserActivityTimeoutOverrideFromWindowManager(
+ mUserActivityTimeout);
+ }
+
+ if (mService.mTurnOnScreen) {
+ if (mService.mAllowTheaterModeWakeFromLayout
+ || Settings.Global.getInt(mService.mContext.getContentResolver(),
+ Settings.Global.THEATER_MODE_ON, 0) == 0) {
+ if (DEBUG_VISIBILITY || DEBUG_POWER) {
+ Slog.v(TAG, "Turning screen on after layout!");
+ }
+ mService.mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+ "android.server.wm:TURN_ON");
+ }
+ mService.mTurnOnScreen = false;
+ }
+
+ if (mUpdateRotation) {
+ if (DEBUG_ORIENTATION) Slog.d(TAG,
+ "Performing post-rotate rotation");
+ if (mService.updateRotationUncheckedLocked(false)) {
+ mService.mH.sendEmptyMessage(SEND_NEW_CONFIGURATION);
+ } else {
+ mUpdateRotation = false;
+ }
+ }
+
+ if (mService.mWaitingForDrawnCallback != null ||
+ (mOrientationChangeComplete && !defaultDisplay.layoutNeeded &&
+ !mUpdateRotation)) {
+ mService.checkDrawnWindowsLocked();
+ }
+
+ final int N = mService.mPendingRemove.size();
+ if (N > 0) {
+ if (mService.mPendingRemoveTmp.length < N) {
+ mService.mPendingRemoveTmp = new WindowState[N+10];
+ }
+ mService.mPendingRemove.toArray(mService.mPendingRemoveTmp);
+ mService.mPendingRemove.clear();
+ DisplayContentList displayList = new DisplayContentList();
+ for (i = 0; i < N; i++) {
+ WindowState w = mService.mPendingRemoveTmp[i];
+ mService.removeWindowInnerLocked(w);
+ final DisplayContent displayContent = w.getDisplayContent();
+ if (displayContent != null && !displayList.contains(displayContent)) {
+ displayList.add(displayContent);
+ }
+ }
+
+ for (DisplayContent displayContent : displayList) {
+ mService.assignLayersLocked(displayContent.getWindowList());
+ displayContent.layoutNeeded = true;
+ }
+ }
+
+ // Remove all deferred displays stacks, tasks, and activities.
+ for (int displayNdx = mService.mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
+ mService.mDisplayContents.valueAt(displayNdx).checkForDeferredActions();
+ }
+
+ if (updateInputWindowsNeeded) {
+ mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+ }
+ mService.setFocusTaskRegion();
+
+ // Check to see if we are now in a state where the screen should
+ // be enabled, because the window obscured flags have changed.
+ mService.enableScreenIfNeededLocked();
+
+ mService.scheduleAnimationLocked();
+
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.e(TAG,
+ "performSurfacePlacementInner exit: animating="
+ + mService.mAnimator.mAnimating);
+ }
+ }
+
+ private void applySurfaceChangesTransaction(boolean recoveringMemory, int numDisplays,
+ int defaultDw, int defaultDh) {
+ if (mService.mWatermark != null) {
+ mService.mWatermark.positionSurface(defaultDw, defaultDh);
+ }
+ if (mService.mStrictModeFlash != null) {
+ mService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
+ }
+ if (mService.mCircularDisplayMask != null) {
+ mService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
+ mService.mRotation);
+ }
+ if (mService.mEmulatorDisplayOverlay != null) {
+ mService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
+ mService.mRotation);
+ }
+
+ boolean focusDisplayed = false;
+
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
+ boolean updateAllDrawn = false;
+ WindowList windows = displayContent.getWindowList();
+ DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int displayId = displayContent.getDisplayId();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+ final int innerDw = displayInfo.appWidth;
+ final int innerDh = displayInfo.appHeight;
+ final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+
+ // Reset for each display.
+ mDisplayHasContent = false;
+ mPreferredRefreshRate = 0;
+ mPreferredModeId = 0;
+
+ int repeats = 0;
+ do {
+ repeats++;
+ if (repeats > 6) {
+ Slog.w(TAG, "Animation repeat aborted after too many iterations");
+ displayContent.layoutNeeded = false;
+ break;
+ }
+
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats(
+ "On entry to LockedInner", displayContent.pendingLayoutChanges);
+
+ if ((displayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0 &&
+ mWallpaperControllerLocked.adjustWallpaperWindows()) {
+ mService.assignLayersLocked(windows);
+ displayContent.layoutNeeded = true;
+ }
+
+ if (isDefaultDisplay
+ && (displayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
+ if (mService.updateOrientationFromAppTokensLocked(true)) {
+ displayContent.layoutNeeded = true;
+ mService.mH.sendEmptyMessage(SEND_NEW_CONFIGURATION);
+ }
+ }
+
+ if ((displayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ displayContent.layoutNeeded = true;
+ }
+
+ // FIRST LOOP: Perform a layout, if needed.
+ if (repeats < LAYOUT_REPEAT_THRESHOLD) {
+ performLayoutLockedInner(displayContent, repeats == 1,
+ false /* updateInputWindows */);
+ } else {
+ Slog.w(TAG, "Layout repeat skipped after too many iterations");
+ }
+
+ // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
+ // it is animating.
+ displayContent.pendingLayoutChanges = 0;
+
+ if (isDefaultDisplay) {
+ mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
+ if (w.mHasSurface) {
+ mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs,
+ w.mAttachedWindow);
+ }
+ }
+ displayContent.pendingLayoutChanges |=
+ mService.mPolicy.finishPostLayoutPolicyLw();
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishPostLayoutPolicyLw",
+ displayContent.pendingLayoutChanges);
+ }
+ } while (displayContent.pendingLayoutChanges != 0);
+
+ mObscured = false;
+ mSyswin = false;
+ displayContent.resetDimming();
+
+ // Only used if default window
+ final boolean someoneLosingFocus = !mService.mLosingFocus.isEmpty();
+
+ 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) {
+ continue;
+ }
+
+ final boolean obscuredChanged = w.mObscured != mObscured;
+
+ // Update effect.
+ w.mObscured = mObscured;
+ if (!mObscured) {
+ handleNotObscuredLocked(w, innerDw, innerDh);
+ }
+
+ if (task != null && !task.getContinueDimming()) {
+ w.handleFlagDimBehind();
+ }
+
+ if (isDefaultDisplay && obscuredChanged
+ && mWallpaperControllerLocked.isWallpaperTarget(w) && w.isVisibleLw()) {
+ // This is the wallpaper target and its obscured state
+ // changed... make sure the current wallaper's visibility
+ // has been updated accordingly.
+ mWallpaperControllerLocked.updateWallpaperVisibility();
+ }
+
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+ // If the window has moved due to its containing content frame changing, then
+ // notify the listeners and optionally animate it.
+ if (w.hasMoved()) {
+ // Frame has moved, containing content frame has also moved, and we're not
+ // currently animating... let's do something.
+ final int left = w.mFrame.left;
+ final int top = w.mFrame.top;
+ if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0) {
+ Animation a = AnimationUtils.loadAnimation(mService.mContext,
+ com.android.internal.R.anim.window_move_from_decor);
+ winAnimator.setAnimation(a);
+ winAnimator.mAnimDw = w.mLastFrame.left - left;
+ winAnimator.mAnimDh = w.mLastFrame.top - top;
+ winAnimator.mAnimateMove = true;
+ winAnimator.mAnimatingMove = true;
+ }
+
+ //TODO (multidisplay): Accessibility supported only for the default display.
+ if (mService.mAccessibilityController != null
+ && displayId == Display.DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ }
+
+ try {
+ w.mClient.moved(left, top);
+ } catch (RemoteException e) {
+ }
+ }
+
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+ w.mContentChanged = false;
+
+ // Moved from updateWindowsAndWallpaperLocked().
+ if (w.mHasSurface) {
+ // Take care of the window being ready to display.
+ final boolean committed = winAnimator.commitFinishDrawingLocked();
+ if (isDefaultDisplay && committed) {
+ if (w.mAttrs.type == TYPE_DREAM) {
+ // HACK: When a dream is shown, it may at that
+ // point hide the lock screen. So we need to
+ // redo the layout to let the phone window manager
+ // make this happen.
+ displayContent.pendingLayoutChanges |=
+ FINISH_LAYOUT_REDO_LAYOUT;
+ if (DEBUG_LAYOUT_REPEATS) {
+ debugLayoutRepeats("dream and commitFinishDrawingLocked true",
+ displayContent.pendingLayoutChanges);
+ }
+ }
+ if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
+ if (DEBUG_WALLPAPER_LIGHT)
+ Slog.v(TAG, "First draw done in potential wallpaper target " + w);
+ mWallpaperMayChange = true;
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ if (DEBUG_LAYOUT_REPEATS) {
+ debugLayoutRepeats("wallpaper and commitFinishDrawingLocked true",
+ displayContent.pendingLayoutChanges);
+ }
+ }
+ }
+ /*
+ * 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);
+ }
+
+ final AppWindowToken atoken = w.mAppToken;
+ if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
+ Slog.d(TAG, "updateWindows: starting " + w
+ + " isOnScreen=" + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
+ }
+ if (atoken != null && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
+ if (atoken.lastTransactionSequence != mService.mTransactionSequence) {
+ atoken.lastTransactionSequence = mService.mTransactionSequence;
+ atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+ atoken.startingDisplayed = false;
+ }
+ if ((w.isOnScreenIgnoringKeyguard()
+ || winAnimator.mAttrType == TYPE_BASE_APPLICATION)
+ && !w.mExiting && !w.mDestroying) {
+ if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
+ Slog.v(TAG, "Eval win " + w + ": isDrawn="
+ + w.isDrawnLw()
+ + ", isAnimating=" + winAnimator.isAnimating());
+ if (!w.isDrawnLw()) {
+ Slog.v(TAG, "Not displayed: s="
+ + winAnimator.mSurfaceControl
+ + " pv=" + w.mPolicyVisibility
+ + " mDrawState=" + winAnimator.drawStateToString()
+ + " ah=" + w.mAttachedHidden
+ + " th=" + atoken.hiddenRequested
+ + " a=" + winAnimator.mAnimating);
+ }
+ }
+ if (w != atoken.startingWindow) {
+ if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
+ atoken.numInterestingWindows++;
+ if (w.isDrawnLw()) {
+ atoken.numDrawnWindows++;
+ if (DEBUG_VISIBILITY || DEBUG_ORIENTATION)
+ Slog.v(TAG, "tokenMayBeDrawn: " + atoken
+ + " freezingScreen="
+ + atoken.mAppAnimator.freezingScreen
+ + " mAppFreezing=" + w.mAppFreezing);
+ updateAllDrawn = true;
+ }
+ }
+ } else if (w.isDrawnLw()) {
+ atoken.startingDisplayed = true;
+ }
+ }
+ }
+
+ if (isDefaultDisplay && someoneLosingFocus && w == mService.mCurrentFocus
+ && w.isDisplayedLw()) {
+ focusDisplayed = true;
+ }
+
+ mService.updateResizingWindows(w);
+ }
+
+ mService.mDisplayManagerInternal.setDisplayProperties(displayId,
+ mDisplayHasContent,
+ mPreferredRefreshRate,
+ mPreferredModeId,
+ true /* inTraversal, must call performTraversalInTrans... below */);
+
+ mService.getDisplayContentLocked(displayId).stopDimmingIfNeeded();
+
+ if (updateAllDrawn) {
+ updateAllDrawnLocked(displayContent);
+ }
+ }
+
+ if (focusDisplayed) {
+ mService.mH.sendEmptyMessage(REPORT_LOSING_FOCUS);
+ }
+
+ // Give the display manager a chance to adjust properties
+ // like display rotation if it needs to.
+ mService.mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
+ }
+
+ boolean isInLayout() {
+ return mInLayout;
+ }
+
+ final void performLayoutLockedInner(final DisplayContent displayContent,
+ boolean initial, boolean updateInputWindows) {
+ if (!displayContent.layoutNeeded) {
+ return;
+ }
+ displayContent.layoutNeeded = false;
+ WindowList windows = displayContent.getWindowList();
+ boolean isDefaultDisplay = displayContent.isDefaultDisplay;
+
+ DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+
+ if (mService.mInputConsumer != null) {
+ mService.mInputConsumer.layout(dw, dh);
+ }
+
+ final int N = windows.size();
+ int i;
+
+ if (DEBUG_LAYOUT) {
+ Slog.v(TAG, "-------------------------------------");
+ Slog.v(TAG, "performLayout: needed="
+ + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
+ }
+
+ mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation);
+ if (isDefaultDisplay) {
+ // Not needed on non-default displays.
+ mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
+ mService.mScreenRect.set(0, 0, dw, dh);
+ }
+
+ mService.mPolicy.getContentRectLw(mService.mTmpContentRect);
+ displayContent.resize(mService.mTmpContentRect);
+
+ int seq = mService.mLayoutSeq+1;
+ if (seq < 0) seq = 0;
+ mService.mLayoutSeq = seq;
+
+ boolean behindDream = false;
+
+ // First perform layout of any root windows (not attached
+ // to another window).
+ int topAttached = -1;
+ for (i = N-1; i >= 0; i--) {
+ final WindowState win = windows.get(i);
+
+ // Don't do layout of a window if it is not visible, or
+ // soon won't be visible, to avoid wasting time and funky
+ // changes while a window is animating away.
+ final boolean gone = (behindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs))
+ || win.isGoneForLayoutLw();
+
+ if (DEBUG_LAYOUT && !win.mLayoutAttached) {
+ Slog.v(TAG, "1ST PASS " + win
+ + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
+ + " mLayoutAttached=" + win.mLayoutAttached
+ + " screen changed=" + win.isConfigChanged());
+ final AppWindowToken atoken = win.mAppToken;
+ if (gone) Slog.v(TAG, " GONE: mViewVisibility="
+ + win.mViewVisibility + " mRelayoutCalled="
+ + win.mRelayoutCalled + " hidden="
+ + win.mRootToken.hidden + " hiddenRequested="
+ + (atoken != null && atoken.hiddenRequested)
+ + " mAttachedHidden=" + win.mAttachedHidden);
+ else Slog.v(TAG, " VIS: mViewVisibility="
+ + win.mViewVisibility + " mRelayoutCalled="
+ + win.mRelayoutCalled + " hidden="
+ + win.mRootToken.hidden + " hiddenRequested="
+ + (atoken != null && atoken.hiddenRequested)
+ + " mAttachedHidden=" + win.mAttachedHidden);
+ }
+
+ // If this view is GONE, then skip it -- keep the current
+ // frame, and let the caller know so they can ignore it
+ // if they want. (We do the normal layout for INVISIBLE
+ // windows, since that means "perform layout as normal,
+ // just don't display").
+ if (!gone || !win.mHaveFrame || win.mLayoutNeeded
+ || ((win.isConfigChanged() || win.setInsetsChanged()) &&
+ ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
+ (win.mHasSurface && win.mAppToken != null &&
+ win.mAppToken.layoutConfigChanges)))) {
+ if (!win.mLayoutAttached) {
+ if (initial) {
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
+ win.mContentChanged = false;
+ }
+ if (win.mAttrs.type == TYPE_DREAM) {
+ // Don't layout windows behind a dream, so that if it
+ // does stuff like hide the status bar we won't get a
+ // bad transition when it goes away.
+ behindDream = true;
+ }
+ win.mLayoutNeeded = false;
+ win.prelayout();
+ mService.mPolicy.layoutWindowLw(win, null);
+ win.mLayoutSeq = seq;
+ if (DEBUG_LAYOUT) Slog.v(TAG,
+ " LAYOUT: mFrame="
+ + win.mFrame + " mContainingFrame="
+ + win.mContainingFrame + " mDisplayFrame="
+ + win.mDisplayFrame);
+ } else {
+ if (topAttached < 0) topAttached = i;
+ }
+ }
+ }
+
+ boolean attachedBehindDream = false;
+
+ // Now perform layout of attached windows, which usually
+ // depend on the position of the window they are attached to.
+ // XXX does not deal with windows that are attached to windows
+ // that are themselves attached.
+ for (i = topAttached; i >= 0; i--) {
+ final WindowState win = windows.get(i);
+
+ if (win.mLayoutAttached) {
+ if (DEBUG_LAYOUT) Slog.v(TAG,
+ "2ND PASS " + win + " mHaveFrame=" + win.mHaveFrame + " mViewVisibility="
+ + win.mViewVisibility + " mRelayoutCalled=" + win.mRelayoutCalled);
+ // If this view is GONE, then skip it -- keep the current
+ // frame, and let the caller know so they can ignore it
+ // if they want. (We do the normal layout for INVISIBLE
+ // windows, since that means "perform layout as normal,
+ // just don't display").
+ if (attachedBehindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs)) {
+ continue;
+ }
+ if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
+ || !win.mHaveFrame || win.mLayoutNeeded) {
+ if (initial) {
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
+ win.mContentChanged = false;
+ }
+ win.mLayoutNeeded = false;
+ win.prelayout();
+ mService.mPolicy.layoutWindowLw(win, win.mAttachedWindow);
+ win.mLayoutSeq = seq;
+ if (DEBUG_LAYOUT) Slog.v(TAG,
+ " LAYOUT: mFrame=" + win.mFrame + " mContainingFrame="
+ + win.mContainingFrame + " mDisplayFrame=" + win.mDisplayFrame);
+ }
+ } else if (win.mAttrs.type == TYPE_DREAM) {
+ // Don't layout windows behind a dream, so that if it
+ // does stuff like hide the status bar we won't get a
+ // bad transition when it goes away.
+ attachedBehindDream = behindDream;
+ }
+ }
+
+ // Window frames may have changed. Tell the input dispatcher about it.
+ mService.mInputMonitor.setUpdateInputWindowsNeededLw();
+ if (updateInputWindows) {
+ mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+ }
+
+ mService.mPolicy.finishLayoutLw();
+ }
+
+ /**
+ * @param windows List of windows on default display.
+ * @return bitmap indicating if another pass through layout must be made.
+ */
+ private int handleAppTransitionReadyLocked(WindowList windows) {
+ int appsCount = mService.mOpeningApps.size();
+ if (!transitionGoodToGo(appsCount)) {
+ return 0;
+ }
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
+ int transit = mService.mAppTransition.getAppTransition();
+ if (mService.mSkipAppTransitionAnimation) {
+ transit = AppTransition.TRANSIT_UNSET;
+ }
+ mService.mSkipAppTransitionAnimation = false;
+ mService.mNoAnimationNotifyOnTransitionFinished.clear();
+
+ mService.mH.removeMessages(APP_TRANSITION_TIMEOUT);
+
+ mService.rebuildAppWindowListLocked();
+
+ mWallpaperMayChange = false;
+
+ // The top-most window will supply the layout params,
+ // and we will determine it below.
+ WindowManager.LayoutParams animLp = null;
+ int bestAnimLayer = -1;
+ boolean fullscreenAnim = false;
+ boolean voiceInteraction = false;
+
+ final WindowState lowerWallpaperTarget =
+ mWallpaperControllerLocked.getLowerWallpaperTarget();
+ final WindowState upperWallpaperTarget =
+ mWallpaperControllerLocked.getUpperWallpaperTarget();
+
+ boolean openingAppHasWallpaper = false;
+ boolean closingAppHasWallpaper = false;
+ final AppWindowToken lowerWallpaperAppToken;
+ final AppWindowToken upperWallpaperAppToken;
+ if (lowerWallpaperTarget == null) {
+ lowerWallpaperAppToken = upperWallpaperAppToken = null;
+ } else {
+ lowerWallpaperAppToken = lowerWallpaperTarget.mAppToken;
+ upperWallpaperAppToken = upperWallpaperTarget.mAppToken;
+ }
+
+ int i;
+ // Do a first pass through the tokens for two
+ // things:
+ // (1) Determine if both the closing and opening
+ // app token sets are wallpaper targets, in which
+ // case special animations are needed
+ // (since the wallpaper needs to stay static
+ // behind them).
+ // (2) Find the layout params of the top-most
+ // application window in the tokens, which is
+ // what will control the animation theme.
+ final int closingAppsCount = mService.mClosingApps.size();
+ appsCount = closingAppsCount + mService.mOpeningApps.size();
+ for (i = 0; i < appsCount; i++) {
+ final AppWindowToken wtoken;
+ if (i < closingAppsCount) {
+ wtoken = mService.mClosingApps.valueAt(i);
+ if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
+ closingAppHasWallpaper = true;
+ }
+ } else {
+ wtoken = mService.mOpeningApps.valueAt(i - closingAppsCount);
+ if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
+ openingAppHasWallpaper = true;
+ }
+ }
+
+ voiceInteraction |= wtoken.voiceInteraction;
+
+ if (wtoken.appFullscreen) {
+ WindowState ws = wtoken.findMainWindow();
+ if (ws != null) {
+ animLp = ws.mAttrs;
+ bestAnimLayer = ws.mLayer;
+ fullscreenAnim = true;
+ }
+ } else if (!fullscreenAnim) {
+ WindowState ws = wtoken.findMainWindow();
+ if (ws != null) {
+ if (ws.mLayer > bestAnimLayer) {
+ animLp = ws.mAttrs;
+ bestAnimLayer = ws.mLayer;
+ }
+ }
+ }
+ }
+
+ transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
+ closingAppHasWallpaper, lowerWallpaperTarget, upperWallpaperTarget);
+
+ // If all closing windows are obscured, then there is
+ // no need to do an animation. This is the case, for
+ // example, when this transition is being done behind
+ // the lock screen.
+ if (!mService.mPolicy.allowAppAnimationsLw()) {
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "Animations disallowed by keyguard or dream.");
+ animLp = null;
+ }
+
+ processApplicationsAnimatingInPlace(transit);
+
+ AppWindowToken topClosingApp = null;
+ int topClosingLayer = 0;
+ appsCount = mService.mClosingApps.size();
+ for (i = 0; i < appsCount; i++) {
+ AppWindowToken wtoken = mService.mClosingApps.valueAt(i);
+ final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "Now closing app " + wtoken);
+ appAnimator.clearThumbnail();
+ appAnimator.animation = null;
+ wtoken.inPendingTransaction = false;
+ mService.setTokenVisibilityLocked(wtoken, animLp, false, transit, false,
+ voiceInteraction);
+ wtoken.updateReportedVisibilityLocked();
+ // Force the allDrawn flag, because we want to start
+ // this guy's animations regardless of whether it's
+ // gotten drawn.
+ wtoken.allDrawn = true;
+ wtoken.deferClearAllDrawn = false;
+ // Ensure that apps that are mid-starting are also scheduled to have their
+ // starting windows removed after the animation is complete
+ if (wtoken.startingWindow != null && !wtoken.startingWindow.mExiting) {
+ mService.scheduleRemoveStartingWindowLocked(wtoken);
+ }
+ mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
+
+ if (animLp != null) {
+ int layer = -1;
+ for (int j = 0; j < wtoken.windows.size(); j++) {
+ WindowState win = wtoken.windows.get(j);
+ if (win.mWinAnimator.mAnimLayer > layer) {
+ layer = win.mWinAnimator.mAnimLayer;
+ }
+ }
+ if (topClosingApp == null || layer > topClosingLayer) {
+ topClosingApp = wtoken;
+ topClosingLayer = layer;
+ }
+ }
+ }
+
+ AppWindowToken topOpeningApp = null;
+ appsCount = mService.mOpeningApps.size();
+ for (i = 0; i < appsCount; i++) {
+ AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
+ final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "Now opening app" + wtoken);
+
+ if (!appAnimator.usingTransferredAnimation) {
+ appAnimator.clearThumbnail();
+ appAnimator.animation = null;
+ }
+ wtoken.inPendingTransaction = false;
+ if (!mService.setTokenVisibilityLocked(
+ wtoken, animLp, true, transit, false, voiceInteraction)){
+ // This token isn't going to be animating. Add it to the list of tokens to
+ // be notified of app transition complete since the notification will not be
+ // sent be the app window animator.
+ mService.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
+ }
+ wtoken.updateReportedVisibilityLocked();
+ wtoken.waitingToShow = false;
+
+ appAnimator.mAllAppWinAnimators.clear();
+ final int windowsCount = wtoken.allAppWindows.size();
+ for (int j = 0; j < windowsCount; j++) {
+ appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
+ }
+ mService.mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
+ mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
+
+ int topOpeningLayer = 0;
+ if (animLp != null) {
+ int layer = -1;
+ for (int j = 0; j < wtoken.windows.size(); j++) {
+ WindowState win = wtoken.windows.get(j);
+ if (win.mWinAnimator.mAnimLayer > layer) {
+ layer = win.mWinAnimator.mAnimLayer;
+ }
+ }
+ if (topOpeningApp == null || layer > topOpeningLayer) {
+ topOpeningApp = wtoken;
+ topOpeningLayer = layer;
+ }
+ }
+ createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+ }
+
+ AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null :
+ topOpeningApp.mAppAnimator;
+ AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null :
+ topClosingApp.mAppAnimator;
+
+ mService.mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator);
+ mService.mAppTransition.postAnimationCallback();
+ mService.mAppTransition.clear();
+
+ mService.mOpeningApps.clear();
+ mService.mClosingApps.clear();
+
+ // This has changed the visibility of windows, so perform
+ // a new layout to get them all up-to-date.
+ mService.getDefaultDisplayContentLocked().layoutNeeded = true;
+
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ if (windows == mService.getDefaultWindowListLocked()
+ && !mService.moveInputMethodWindowsIfNeededLocked(true)) {
+ mService.assignLayersLocked(windows);
+ }
+ mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+ true /*updateInputWindows*/);
+ mService.mFocusMayChange = false;
+ mService.notifyActivityDrawnForKeyguard();
+ return FINISH_LAYOUT_REDO_LAYOUT
+ | FINISH_LAYOUT_REDO_CONFIG;
+
+ }
+
+ private boolean transitionGoodToGo(int appsCount) {
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "Checking " + appsCount + " opening apps (frozen="
+ + mService.mDisplayFrozen + " timeout="
+ + mService.mAppTransition.isTimeout() + ")...");
+ if (!mService.mAppTransition.isTimeout()) {
+ for (int i = 0; i < appsCount; i++) {
+ AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "Check opening app=" + wtoken + ": allDrawn="
+ + wtoken.allDrawn + " startingDisplayed="
+ + wtoken.startingDisplayed + " startingMoved="
+ + wtoken.startingMoved);
+ if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
+ return false;
+ }
+ }
+
+ // If the wallpaper is visible, we need to check it's ready too.
+ return !mWallpaperControllerLocked.isWallpaperVisible() ||
+ mWallpaperControllerLocked.wallpaperTransitionReady();
+ }
+ return true;
+ }
+
+ private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
+ boolean closingAppHasWallpaper, WindowState lowerWallpaperTarget,
+ WindowState upperWallpaperTarget) {
+ // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper
+ final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
+ final WindowState oldWallpaper =
+ mWallpaperControllerLocked.isWallpaperTargetAnimating()
+ ? null : wallpaperTarget;
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "New wallpaper target=" + wallpaperTarget
+ + ", oldWallpaper=" + oldWallpaper
+ + ", lower target=" + lowerWallpaperTarget
+ + ", upper target=" + upperWallpaperTarget);
+ mService.mAnimateWallpaperWithTarget = false;
+ if (closingAppHasWallpaper && openingAppHasWallpaper) {
+ if (DEBUG_APP_TRANSITIONS)
+ Slog.v(TAG, "Wallpaper animation!");
+ switch (transit) {
+ case AppTransition.TRANSIT_ACTIVITY_OPEN:
+ case AppTransition.TRANSIT_TASK_OPEN:
+ case AppTransition.TRANSIT_TASK_TO_FRONT:
+ transit = AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN;
+ break;
+ case AppTransition.TRANSIT_ACTIVITY_CLOSE:
+ case AppTransition.TRANSIT_TASK_CLOSE:
+ case AppTransition.TRANSIT_TASK_TO_BACK:
+ transit = AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE;
+ break;
+ }
+ 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.
+ 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()) {
+ // We are transitioning from an activity without
+ // a wallpaper to now showing the wallpaper
+ transit = AppTransition.TRANSIT_WALLPAPER_OPEN;
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "New transit into wallpaper: "
+ + AppTransition.appTransitionToString(transit));
+ } else {
+ mService.mAnimateWallpaperWithTarget = true;
+ }
+ return transit;
+ }
+
+ /**
+ * @param w WindowState this method is applied to.
+ * @param innerDw Width of app window.
+ * @param innerDh Height of app window.
+ */
+ private void handleNotObscuredLocked(final WindowState w, final int innerDw, final int innerDh) {
+ final WindowManager.LayoutParams attrs = w.mAttrs;
+ final int attrFlags = attrs.flags;
+ final boolean canBeSeen = w.isDisplayedLw();
+ final boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
+
+ if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) {
+ // This window completely covers everything behind it,
+ // so we want to leave all of them as undimmed (for
+ // performance reasons).
+ mObscured = true;
+ }
+
+ if (w.mHasSurface) {
+ if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
+ mHoldScreen = w.mSession;
+ }
+ if (!mSyswin && w.mAttrs.screenBrightness >= 0
+ && mScreenBrightness < 0) {
+ mScreenBrightness = w.mAttrs.screenBrightness;
+ }
+ if (!mSyswin && w.mAttrs.buttonBrightness >= 0
+ && mButtonBrightness < 0) {
+ mButtonBrightness = w.mAttrs.buttonBrightness;
+ }
+ if (!mSyswin && w.mAttrs.userActivityTimeout >= 0
+ && mUserActivityTimeout < 0) {
+ mUserActivityTimeout = w.mAttrs.userActivityTimeout;
+ }
+
+ final int type = attrs.type;
+ if (canBeSeen
+ && (type == TYPE_SYSTEM_DIALOG
+ || type == TYPE_SYSTEM_ERROR
+ || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0)) {
+ mSyswin = true;
+ }
+
+ if (canBeSeen) {
+ // This function assumes that the contents of the default display are
+ // processed first before secondary displays.
+ final DisplayContent displayContent = w.getDisplayContent();
+ if (displayContent != null && displayContent.isDefaultDisplay) {
+ // While a dream or keyguard is showing, obscure ordinary application
+ // content on secondary displays (by forcibly enabling mirroring unless
+ // there is other content we want to show) but still allow opaque
+ // keyguard dialogs to be shown.
+ if (type == TYPE_DREAM || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+ mObscureApplicationContentOnSecondaryDisplays = true;
+ }
+ mDisplayHasContent = true;
+ } else if (displayContent != null &&
+ (!mObscureApplicationContentOnSecondaryDisplays
+ || (mObscured && type == TYPE_KEYGUARD_DIALOG))) {
+ // Allow full screen keyguard presentation dialogs to be seen.
+ mDisplayHasContent = true;
+ }
+ if (mPreferredRefreshRate == 0
+ && w.mAttrs.preferredRefreshRate != 0) {
+ mPreferredRefreshRate = w.mAttrs.preferredRefreshRate;
+ }
+ if (mPreferredModeId == 0
+ && w.mAttrs.preferredDisplayModeId != 0) {
+ mPreferredModeId = w.mAttrs.preferredDisplayModeId;
+ }
+ }
+ }
+ }
+
+ private void updateAllDrawnLocked(DisplayContent displayContent) {
+ // See if any windows have been drawn, so they (and others
+ // associated with them) can now be shown.
+ ArrayList<TaskStack> stacks = displayContent.getStacks();
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ArrayList<Task> tasks = stacks.get(stackNdx).getTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
+ for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
+ final AppWindowToken wtoken = tokens.get(tokenNdx);
+ if (!wtoken.allDrawn) {
+ int numInteresting = wtoken.numInterestingWindows;
+ if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+ if (DEBUG_VISIBILITY)
+ Slog.v(TAG, "allDrawn: " + wtoken
+ + " interesting=" + numInteresting
+ + " drawn=" + wtoken.numDrawnWindows);
+ wtoken.allDrawn = true;
+ // Force an additional layout pass where WindowStateAnimator#
+ // commitFinishDrawingLocked() will call performShowLocked().
+ displayContent.layoutNeeded = true;
+ mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN,
+ wtoken.token).sendToTarget();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static int toBrightnessOverride(float value) {
+ return (int)(value * PowerManager.BRIGHTNESS_ON);
+ }
+
+ private void processApplicationsAnimatingInPlace(int transit) {
+ if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) {
+ // Find the focused window
+ final WindowState win = mService.findFocusedWindowLocked(
+ mService.getDefaultDisplayContentLocked());
+ if (win != null) {
+ final AppWindowToken wtoken = win.mAppToken;
+ final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
+ if (DEBUG_APP_TRANSITIONS)
+ Slog.v(TAG, "Now animating app in place " + wtoken);
+ appAnimator.clearThumbnail();
+ appAnimator.animation = null;
+ mService.updateTokenInPlaceLocked(wtoken, transit);
+ wtoken.updateReportedVisibilityLocked();
+
+ appAnimator.mAllAppWinAnimators.clear();
+ final int N = wtoken.allAppWindows.size();
+ for (int j = 0; j < N; j++) {
+ appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
+ }
+ mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
+ mService.mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
+ }
+ }
+ }
+
+ private void createThumbnailAppAnimator(int transit, AppWindowToken appToken,
+ int openingLayer, int closingLayer) {
+ AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator;
+ if (openingAppAnimator == null || openingAppAnimator.animation == null) {
+ return;
+ }
+ final int taskId = appToken.mTask.mTaskId;
+ Bitmap thumbnailHeader = mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
+ if (thumbnailHeader == null || thumbnailHeader.getConfig() == Bitmap.Config.ALPHA_8) {
+ return;
+ }
+ // This thumbnail animation is very special, we need to have
+ // an extra surface with the thumbnail included with the animation.
+ Rect dirty = new Rect(0, 0, thumbnailHeader.getWidth(), thumbnailHeader.getHeight());
+ try {
+ // TODO(multi-display): support other displays
+ final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
+ final Display display = displayContent.getDisplay();
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+
+ // Create a new surface for the thumbnail
+ SurfaceControl surfaceControl = new SurfaceControl(mService.mFxSession,
+ "thumbnail anim", dirty.width(), dirty.height(),
+ PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ surfaceControl.setLayerStack(display.getLayerStack());
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG, " THUMBNAIL " + surfaceControl + ": CREATE");
+ }
+
+ // Draw the thumbnail onto the surface
+ Surface drawSurface = new Surface();
+ drawSurface.copyFrom(surfaceControl);
+ Canvas c = drawSurface.lockCanvas(dirty);
+ c.drawBitmap(thumbnailHeader, 0, 0, null);
+ drawSurface.unlockCanvasAndPost(c);
+ drawSurface.release();
+
+ // Get the thumbnail animation
+ Animation anim;
+ if (mService.mAppTransition.isNextThumbnailTransitionAspectScaled()) {
+ // If this is a multi-window scenario, we use the windows frame as
+ // destination of the thumbnail header animation. If this is a full screen
+ // window scenario, we use the whole display as the target.
+ WindowState win = appToken.findMainWindow();
+ Rect appRect = win != null ? win.getContentFrameLw() :
+ new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
+ // For the new aspect-scaled transition, we want it to always show
+ // above the animating opening/closing window, and we want to
+ // synchronize its thumbnail surface with the surface for the
+ // open/close animation (only on the way down)
+ anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
+ thumbnailHeader, taskId);
+ openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
+ openingAppAnimator.deferThumbnailDestruction =
+ !mService.mAppTransition.isNextThumbnailTransitionScaleUp();
+ } else {
+ anim = mService.mAppTransition.createThumbnailScaleAnimationLocked(
+ displayInfo.appWidth, displayInfo.appHeight, transit, thumbnailHeader);
+ }
+ anim.restrictDuration(MAX_ANIMATION_DURATION);
+ anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
+
+ openingAppAnimator.thumbnail = surfaceControl;
+ openingAppAnimator.thumbnailLayer = openingLayer;
+ openingAppAnimator.thumbnailAnimation = anim;
+ mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
+ openingAppAnimator.thumbnailX = mTmpStartRect.left;
+ openingAppAnimator.thumbnailY = mTmpStartRect.top;
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
+ + dirty.width() + " h=" + dirty.height(), e);
+ openingAppAnimator.clearThumbnail();
+ }
+ }
+
+ boolean copyAnimToLayoutParamsLocked() {
+ boolean doRequest = false;
+
+ final int bulkUpdateParams = mService.mAnimator.mBulkUpdateParams;
+ if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
+ mUpdateRotation = true;
+ doRequest = true;
+ }
+ if ((bulkUpdateParams & SET_WALLPAPER_MAY_CHANGE) != 0) {
+ mWallpaperMayChange = true;
+ doRequest = true;
+ }
+ if ((bulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0) {
+ mWallpaperForceHidingChanged = true;
+ doRequest = true;
+ }
+ if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
+ mOrientationChangeComplete = false;
+ } else {
+ mOrientationChangeComplete = true;
+ mLastWindowFreezeSource = mService.mAnimator.mLastWindowFreezeSource;
+ if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
+ doRequest = true;
+ }
+ }
+ if ((bulkUpdateParams & SET_TURN_ON_SCREEN) != 0) {
+ mService.mTurnOnScreen = true;
+ }
+ if ((bulkUpdateParams & SET_WALLPAPER_ACTION_PENDING) != 0) {
+ mWallpaperActionPending = true;
+ }
+
+ return doRequest;
+ }
+
+ void requestTraversal() {
+ if (!mTraversalScheduled) {
+ mTraversalScheduled = true;
+ mService.mH.sendEmptyMessage(DO_TRAVERSAL);
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mTraversalScheduled="); pw.println(mTraversalScheduled);
+ }
+}
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 9556b08..98d8d08 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -4,9 +4,16 @@
LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter
+ifneq ($(ENABLE_CPUSETS),)
+ifneq ($(ENABLE_SCHED_BOOST),)
+LOCAL_CFLAGS += -DUSE_SCHED_BOOST
+endif
+endif
+
LOCAL_SRC_FILES += \
$(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_am_ActivityManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
$(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
$(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
diff --git a/services/core/jni/com_android_server_am_ActivityManagerService.cpp b/services/core/jni/com_android_server_am_ActivityManagerService.cpp
new file mode 100644
index 0000000..52217b9
--- /dev/null
+++ b/services/core/jni/com_android_server_am_ActivityManagerService.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ActivityManagerService"
+//#define LOG_NDEBUG 0
+
+#include <android_runtime/AndroidRuntime.h>
+#include <jni.h>
+
+#include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
+
+#include <cutils/log.h>
+#include <utils/misc.h>
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android
+{
+
+ // migrate from foreground to foreground_boost
+ static jint migrateToBoost(JNIEnv *env, jobject _this)
+ {
+#ifdef USE_SCHED_BOOST
+ // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
+ FILE* fg_cpuset_file = NULL;
+ int boost_cpuset_fd = 0;
+ if (!access("/dev/cpuset/tasks", F_OK)) {
+ fg_cpuset_file = fopen("/dev/cpuset/foreground/tasks", "r+");
+ if (ferror(fg_cpuset_file)) {
+ return 0;
+ }
+ boost_cpuset_fd = open("/dev/cpuset/foreground/boost/tasks", O_WRONLY);
+ if (boost_cpuset_fd < 0) {
+ fclose(fg_cpuset_file);
+ return 0;
+ }
+
+ }
+ if (!fg_cpuset_file || !boost_cpuset_fd) {
+ fclose(fg_cpuset_file);
+ close(boost_cpuset_fd);
+ return 0;
+ }
+ char buf[17];
+ while (fgets(buf, 16, fg_cpuset_file)) {
+ int i = 0;
+ for (; i < 16; i++) {
+ if (buf[i] == '\n') {
+ buf[i] = 0;
+ break;
+ }
+ }
+ if (write(boost_cpuset_fd, buf, i) < 0) {
+ // ignore error
+ }
+ if (feof(fg_cpuset_file))
+ break;
+ }
+ fclose(fg_cpuset_file);
+ close(boost_cpuset_fd);
+#endif
+ return 0;
+ }
+
+ // migrate from foreground_boost to foreground
+ static jint migrateFromBoost(JNIEnv *env, jobject _this)
+ {
+#ifdef USE_SCHED_BOOST
+ // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
+ int fg_cpuset_fd = 0;
+ FILE* boost_cpuset_file = NULL;
+ if (!access("/dev/cpuset/tasks", F_OK)) {
+ boost_cpuset_file = fopen("/dev/cpuset/foreground/boost/tasks", "r+");
+ if (ferror(boost_cpuset_file)) {
+ return 0;
+ }
+ fg_cpuset_fd = open("/dev/cpuset/foreground/tasks", O_WRONLY);
+ if (fg_cpuset_fd < 0) {
+ fclose(boost_cpuset_file);
+ return 0;
+ }
+
+ }
+ if (!boost_cpuset_file || !fg_cpuset_fd) {
+ fclose(boost_cpuset_file);
+ close(fg_cpuset_fd);
+ 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;
+ for (; i < 16; i++) {
+ if (buf[i] == '\n') {
+ buf[i] = 0;
+ break;
+ }
+ }
+ if (write(fg_cpuset_fd, buf, i) < 0) {
+ //ALOGE("Appending FD %s to fg ERROR", buf);
+ // handle error?
+ }
+ if (feof(boost_cpuset_file))
+ break;
+ }
+
+ close(fg_cpuset_fd);
+ fclose(boost_cpuset_file);
+
+#endif
+ return 0;
+
+ }
+
+
+ static JNINativeMethod method_table[] = {
+ { "nativeMigrateToBoost", "()I", (void*)migrateToBoost },
+ { "nativeMigrateFromBoost", "()I", (void*)migrateFromBoost },
+ };
+
+ int register_android_server_ActivityManagerService(JNIEnv *env)
+ {
+ return jniRegisterNativeMethods(env, "com/android/server/am/ActivityManagerService",
+ method_table, NELEM(method_table));
+ }
+
+}
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 67872da..1f3fde6 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -20,6 +20,7 @@
#include "utils/misc.h"
namespace android {
+int register_android_server_ActivityManagerService(JNIEnv* env);
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
int register_android_server_BatteryStatsService(JNIEnv* env);
@@ -57,6 +58,7 @@
}
ALOG_ASSERT(env, "Could not retrieve the env!");
+ register_android_server_ActivityManagerService(env);
register_android_server_PowerManagerService(env);
register_android_server_SerialService(env);
register_android_server_InputApplicationHandle(env);
@@ -80,5 +82,6 @@
register_android_server_PersistentDataBlockService(env);
register_android_server_Watchdog(env);
+
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 00a3c5c..67e5703 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -45,7 +45,6 @@
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SystemUpdatePolicy;
import android.app.backup.IBackupManager;
-import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -97,6 +96,8 @@
import android.security.KeyChain.KeyChainConnection;
import android.service.persistentdata.PersistentDataBlockManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -141,8 +142,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
@@ -198,7 +197,7 @@
private static final Set<String> DEVICE_OWNER_USER_RESTRICTIONS;
static {
- DEVICE_OWNER_USER_RESTRICTIONS = new HashSet();
+ DEVICE_OWNER_USER_RESTRICTIONS = new ArraySet<>();
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_USB_FILE_TRANSFER);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_TETHERING);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_NETWORK_RESET);
@@ -219,7 +218,7 @@
// owner and profile owner.
private static final Set<String> IMMUTABLE_USER_RESTRICTIONS;
static {
- IMMUTABLE_USER_RESTRICTIONS = new HashSet();
+ IMMUTABLE_USER_RESTRICTIONS = new ArraySet<>();
IMMUTABLE_USER_RESTRICTIONS.add(UserManager.DISALLOW_WALLPAPER);
}
@@ -228,16 +227,16 @@
private static final Set<String> GLOBAL_SETTINGS_WHITELIST;
private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
static {
- SECURE_SETTINGS_WHITELIST = new HashSet();
+ SECURE_SETTINGS_WHITELIST = new ArraySet<>();
SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
SECURE_SETTINGS_WHITELIST.add(Settings.Secure.SKIP_FIRST_USE_HINTS);
SECURE_SETTINGS_WHITELIST.add(Settings.Secure.INSTALL_NON_MARKET_APPS);
- SECURE_SETTINGS_DEVICEOWNER_WHITELIST = new HashSet();
+ SECURE_SETTINGS_DEVICEOWNER_WHITELIST = new ArraySet<>();
SECURE_SETTINGS_DEVICEOWNER_WHITELIST.addAll(SECURE_SETTINGS_WHITELIST);
SECURE_SETTINGS_DEVICEOWNER_WHITELIST.add(Settings.Secure.LOCATION_MODE);
- GLOBAL_SETTINGS_WHITELIST = new HashSet();
+ GLOBAL_SETTINGS_WHITELIST = new ArraySet<>();
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_ENABLED);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME_ZONE);
@@ -247,7 +246,7 @@
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
- GLOBAL_SETTINGS_DEPRECATED = new HashSet();
+ GLOBAL_SETTINGS_DEPRECATED = new ArraySet<>();
GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.BLUETOOTH_ON);
GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.MODE_RINGER);
@@ -279,7 +278,7 @@
NotificationManager mNotificationManager;
// Stores and loads state on device and profile owners.
- private final DeviceOwner mDeviceOwner;
+ private final Owners mOwners;
private final Binder mToken = new Binder();
@@ -304,9 +303,7 @@
@Override
public void onBootPhase(int phase) {
- if (phase == PHASE_LOCK_SETTINGS_READY) {
- mService.systemReady();
- }
+ mService.systemReady(phase);
}
}
@@ -327,7 +324,7 @@
boolean mUserSetupComplete = false;
int mPermissionPolicy;
- final HashMap<ComponentName, ActiveAdmin> mAdminMap = new HashMap<>();
+ final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
@@ -495,7 +492,7 @@
}
}
- Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
+ Set<String> accountTypesWithManagementDisabled = new ArraySet<>();
// The list of permitted accessibility services package namesas set by a profile
// or device owner. Null means all accessibility services are allowed, empty means
@@ -512,7 +509,7 @@
String globalProxySpec = null;
String globalProxyExclusionList = null;
- HashMap<String, TrustAgentInfo> trustAgentInfos = new HashMap<String, TrustAgentInfo>();
+ ArrayMap<String, TrustAgentInfo> trustAgentInfos = new ArrayMap<>();
List<String> crossProfileWidgetProviders;
@@ -835,7 +832,7 @@
throws XmlPullParserException, IOException {
int outerDepthDAM = parser.getDepth();
int typeDAM;
- Set<String> result = new HashSet<String>();
+ Set<String> result = new ArraySet<>();
while ((typeDAM=parser.next()) != END_DOCUMENT
&& (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
if (typeDAM == END_TAG || typeDAM == TEXT) {
@@ -851,11 +848,11 @@
return result;
}
- private HashMap<String, TrustAgentInfo> getAllTrustAgentInfos(
+ private ArrayMap<String, TrustAgentInfo> getAllTrustAgentInfos(
XmlPullParser parser, String tag) throws XmlPullParserException, IOException {
int outerDepthDAM = parser.getDepth();
int typeDAM;
- HashMap<String, TrustAgentInfo> result = new HashMap<String, TrustAgentInfo>();
+ final ArrayMap<String, TrustAgentInfo> result = new ArrayMap<>();
while ((typeDAM=parser.next()) != END_DOCUMENT
&& (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
if (typeDAM == END_TAG || typeDAM == TEXT) {
@@ -1044,7 +1041,7 @@
*/
public DevicePolicyManagerService(Context context) {
mContext = context;
- mDeviceOwner = new DeviceOwner(mContext);
+ mOwners = new Owners(mContext);
mUserManager = UserManager.get(mContext);
mHasFeature = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_DEVICE_ADMIN);
@@ -1120,8 +1117,8 @@
Slog.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
return;
}
- mDeviceOwner.removeProfileOwner(userHandle);
- mDeviceOwner.writeProfileOwner(userHandle);
+ mOwners.removeProfileOwner(userHandle);
+ mOwners.writeProfileOwner(userHandle);
DevicePolicyData policy = mUserData.get(userHandle);
if (policy != null) {
@@ -1135,9 +1132,9 @@
updateScreenCaptureDisabledInWindowManager(userHandle, false /* default value */);
}
- void loadDeviceOwner() {
+ void loadOwners() {
synchronized (this) {
- mDeviceOwner.load();
+ mOwners.load();
updateDeviceOwnerLocked();
}
}
@@ -1777,12 +1774,23 @@
}
}
- public void systemReady() {
+ public void systemReady(int phase) {
if (!mHasFeature) {
return;
}
+ switch (phase) {
+ case SystemService.PHASE_LOCK_SETTINGS_READY:
+ onLockSettingsReady();
+ break;
+ case SystemService.PHASE_BOOT_COMPLETED:
+ ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
+ break;
+ }
+ }
+
+ private void onLockSettingsReady() {
getUserData(UserHandle.USER_OWNER);
- loadDeviceOwner();
+ loadOwners();
cleanUpOldUsers();
// Register an observer for watching for user setup complete.
new SetupContentObserver(mHandler).register(mContext.getContentResolver());
@@ -1799,21 +1807,41 @@
}
}
+ 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);
+
+ // STOPSHIP Prevent the DO user from being killed.
+
+ } catch (RemoteException e) {
+ Slog.w(LOG_TAG, "Exception starting user", e);
+ }
+ }
+ }
+ }
+
private void cleanUpOldUsers() {
// This is needed in case the broadcast {@link Intent.ACTION_USER_REMOVED} was not handled
// before reboot
Set<Integer> usersWithProfileOwners;
Set<Integer> usersWithData;
synchronized(this) {
- usersWithProfileOwners = mDeviceOwner.getProfileOwnerKeys();
- usersWithData = new HashSet<Integer>();
+ usersWithProfileOwners = mOwners.getProfileOwnerKeys();
+ usersWithData = new ArraySet<>();
for (int i = 0; i < mUserData.size(); i++) {
usersWithData.add(mUserData.keyAt(i));
}
}
List<UserInfo> allUsers = mUserManager.getUsers();
- Set<Integer> deletedUsers = new HashSet<Integer>();
+ Set<Integer> deletedUsers = new ArraySet<>();
deletedUsers.addAll(usersWithProfileOwners);
deletedUsers.addAll(usersWithData);
for (UserInfo userInfo : allUsers) {
@@ -4111,17 +4139,17 @@
}
@Override
- public boolean setDeviceOwner(String packageName, String ownerName) {
+ public boolean setDeviceOwner(String packageName, String ownerName, int userId) {
if (!mHasFeature) {
return false;
}
if (packageName == null
- || !DeviceOwner.isInstalled(packageName, mContext.getPackageManager())) {
+ || !Owners.isInstalledForUser(packageName, userId)) {
throw new IllegalArgumentException("Invalid package name " + packageName
+ " for device owner");
}
synchronized (this) {
- enforceCanSetDeviceOwner();
+ enforceCanSetDeviceOwner(userId);
// Shutting down backup manager service permanently.
long ident = Binder.clearCallingIdentity();
@@ -4135,14 +4163,15 @@
Binder.restoreCallingIdentity(ident);
}
- mDeviceOwner.setDeviceOwner(packageName, ownerName);
- mDeviceOwner.writeDeviceOwner();
+ mOwners.setDeviceOwner(packageName, ownerName, userId);
+ mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
ident = Binder.clearCallingIdentity();
try {
- mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+ // TODO Send to system too?
+ mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4156,8 +4185,8 @@
return false;
}
synchronized (this) {
- return mDeviceOwner.hasDeviceOwner()
- && mDeviceOwner.getDeviceOwnerPackageName().equals(packageName);
+ return mOwners.hasDeviceOwner()
+ && mOwners.getDeviceOwnerPackageName().equals(packageName);
}
}
@@ -4167,7 +4196,7 @@
return null;
}
synchronized (this) {
- return mDeviceOwner.getDeviceOwnerPackageName();
+ return mOwners.getDeviceOwnerPackageName();
}
}
@@ -4178,10 +4207,10 @@
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
synchronized (this) {
- if (!mDeviceOwner.hasDeviceOwner()) {
+ if (!mOwners.hasDeviceOwner()) {
return null;
}
- String deviceOwnerPackage = mDeviceOwner.getDeviceOwnerPackageName();
+ String deviceOwnerPackage = mOwners.getDeviceOwnerPackageName();
return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_OWNER);
}
}
@@ -4221,8 +4250,8 @@
synchronized (this) {
clearUserPoliciesLocked(new UserHandle(UserHandle.USER_OWNER));
- mDeviceOwner.clearDeviceOwner();
- mDeviceOwner.writeDeviceOwner();
+ mOwners.clearDeviceOwner();
+ mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
// Reactivate backup service.
long ident = Binder.clearCallingIdentity();
@@ -4243,10 +4272,12 @@
if (!mHasFeature) {
return false;
}
- if (initializer == null || !DeviceOwner.isInstalled(
- initializer.getPackageName(), mContext.getPackageManager())) {
+ if (initializer == null ||
+ !mOwners.hasDeviceOwner() ||
+ !Owners.isInstalledForUser(initializer.getPackageName(),
+ mOwners.getDeviceOwnerUserId())) {
throw new IllegalArgumentException("Invalid component name " + initializer
- + " for device initializer");
+ + " for device initializer or no device owner set");
}
boolean isInitializerSystemApp;
try {
@@ -4262,15 +4293,15 @@
synchronized (this) {
enforceCanSetDeviceInitializer(who);
- if (mDeviceOwner.hasDeviceInitializer()) {
+ if (mOwners.hasDeviceInitializer()) {
throw new IllegalStateException(
"Trying to set device initializer but device initializer is already set.");
}
- mDeviceOwner.setDeviceInitializer(initializer);
+ mOwners.setDeviceInitializer(initializer);
- addDeviceInitializerToLockTaskPackagesLocked(UserHandle.USER_OWNER);
- mDeviceOwner.writeDeviceOwner();
+ addDeviceInitializerToLockTaskPackagesLocked(mOwners.getDeviceOwnerUserId());
+ mOwners.writeDeviceOwner();
return true;
}
}
@@ -4279,7 +4310,7 @@
if (who == null) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
- if (hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+ if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
throw new IllegalStateException(
"Trying to set device initializer but device is already provisioned.");
}
@@ -4294,8 +4325,8 @@
return false;
}
synchronized (this) {
- return mDeviceOwner.hasDeviceInitializer()
- && mDeviceOwner.getDeviceInitializerPackageName().equals(packageName);
+ return mOwners.hasDeviceInitializer()
+ && mOwners.getDeviceInitializerPackageName().equals(packageName);
}
}
@@ -4305,8 +4336,8 @@
return null;
}
synchronized (this) {
- if (mDeviceOwner.hasDeviceInitializer()) {
- return mDeviceOwner.getDeviceInitializerPackageName();
+ if (mOwners.hasDeviceInitializer()) {
+ return mOwners.getDeviceInitializerPackageName();
}
}
return null;
@@ -4318,8 +4349,8 @@
return null;
}
synchronized (this) {
- if (mDeviceOwner.hasDeviceInitializer()) {
- return mDeviceOwner.getDeviceInitializerComponent();
+ if (mOwners.hasDeviceInitializer()) {
+ return mOwners.getDeviceInitializerComponent();
}
}
return null;
@@ -4347,8 +4378,8 @@
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
- mDeviceOwner.clearDeviceInitializer();
- mDeviceOwner.writeDeviceOwner();
+ mOwners.clearDeviceInitializer();
+ mOwners.writeDeviceOwner();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4361,14 +4392,14 @@
return false;
}
if (who == null
- || !DeviceOwner.isInstalledForUser(who.getPackageName(), userHandle)) {
+ || !Owners.isInstalledForUser(who.getPackageName(), userHandle)) {
throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
synchronized (this) {
enforceCanSetProfileOwner(userHandle);
- mDeviceOwner.setProfileOwner(who, ownerName, userHandle);
- mDeviceOwner.writeProfileOwner(userHandle);
+ mOwners.setProfileOwner(who, ownerName, userHandle);
+ mOwners.writeProfileOwner(userHandle);
return true;
}
}
@@ -4384,8 +4415,8 @@
synchronized (this) {
clearUserPoliciesLocked(callingUser);
final int userId = callingUser.getIdentifier();
- mDeviceOwner.removeProfileOwner(userId);
- mDeviceOwner.writeProfileOwner(userId);
+ mOwners.removeProfileOwner(userId);
+ mOwners.writeProfileOwner(userId);
}
}
@@ -4542,14 +4573,14 @@
}
synchronized (this) {
- return mDeviceOwner.getProfileOwnerComponent(userHandle);
+ return mOwners.getProfileOwnerComponent(userHandle);
}
}
// Returns the active profile owner for this user or null if the current user has no
// profile owner.
private ActiveAdmin getProfileOwnerAdmin(int userHandle) {
- ComponentName profileOwner = mDeviceOwner.getProfileOwnerComponent(userHandle);
+ ComponentName profileOwner = mOwners.getProfileOwnerComponent(userHandle);
if (profileOwner == null) {
return null;
}
@@ -4649,31 +4680,45 @@
* The device owner can only be set before the setup phase of the primary user has completed,
* except for adb if no accounts or additional users are present on the device.
*/
- private void enforceCanSetDeviceOwner() {
- if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) {
+ private void enforceCanSetDeviceOwner(int userId) {
+ if (mOwners.hasDeviceOwner()) {
throw new IllegalStateException("Trying to set the device owner, but device owner "
+ "is already set.");
}
+ if (!mUserManager.isUserRunning(new UserHandle(userId))) {
+ throw new IllegalStateException("User not running: " + userId);
+ }
+
int callingUid = Binder.getCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) {
return;
}
- if (mUserManager.getUserCount() > 1) {
- throw new IllegalStateException("Not allowed to set the device owner because there "
- + "are already several users on the device");
- }
- if (AccountManager.get(mContext).getAccounts().length > 0) {
- throw new IllegalStateException("Not allowed to set the device owner because there "
- + "are already some accounts on the device");
+ // STOPSHIP Do proper check in split user mode
+ if (!UserManager.isSplitSystemUser()) {
+ if (mUserManager.getUserCount() > 1) {
+ throw new IllegalStateException(
+ "Not allowed to set the device owner because there "
+ + "are already several users on the device");
+ }
+ if (AccountManager.get(mContext).getAccounts().length > 0) {
+ throw new IllegalStateException(
+ "Not allowed to set the device owner because there "
+ + "are already some accounts on the device");
+ }
}
return;
}
+ // STOPSHIP check the caller UID with userId
+
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
- if (hasUserSetupCompleted(UserHandle.USER_OWNER)) {
- throw new IllegalStateException("Cannot set the device owner if the device is "
- + "already set-up");
+ // STOPSHIP Do proper check in split user mode
+ if (!UserManager.isSplitSystemUser()) {
+ if (hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+ throw new IllegalStateException("Cannot set the device owner if the device is "
+ + "already set-up");
+ }
}
}
@@ -4690,12 +4735,6 @@
}
}
- private void enforceSystemProcess(String message) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException(message);
- }
- }
-
private void enforceNotManagedProfile(int userHandle, String message) {
if(isManagedProfile(userHandle)) {
throw new SecurityException("You can not " + message + " for a managed profile. ");
@@ -4751,7 +4790,7 @@
synchronized (this) {
p.println("Current Device Policy Manager state:");
- mDeviceOwner.dump(" ", pw);
+ mOwners.dump(" ", pw);
int userCount = mUserData.size();
for (int u = 0; u < userCount; u++) {
DevicePolicyData policy = getUserData(mUserData.keyAt(u));
@@ -5697,7 +5736,7 @@
synchronized (this) {
DevicePolicyData policy = getUserData(userId);
final int N = policy.mAdminList.size();
- HashSet<String> resultSet = new HashSet<String>();
+ ArraySet<String> resultSet = new ArraySet<>();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
resultSet.addAll(admin.accountTypesWithManagementDisabled);
@@ -6238,10 +6277,10 @@
@Override
public List<String> getCrossProfileWidgetProviders(int profileId) {
synchronized (DevicePolicyManagerService.this) {
- if (mDeviceOwner == null) {
+ if (mOwners == null) {
return Collections.emptyList();
}
- ComponentName ownerComponent = mDeviceOwner.getProfileOwnerComponent(profileId);
+ ComponentName ownerComponent = mOwners.getProfileOwnerComponent(profileId);
if (ownerComponent == null) {
return Collections.emptyList();
}
@@ -6311,21 +6350,21 @@
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
if (policy == null) {
- mDeviceOwner.clearSystemUpdatePolicy();
+ mOwners.clearSystemUpdatePolicy();
} else {
- mDeviceOwner.setSystemUpdatePolicy(policy);
+ mOwners.setSystemUpdatePolicy(policy);
}
- mDeviceOwner.writeDeviceOwner();
+ mOwners.writeDeviceOwner();
}
mContext.sendBroadcastAsUser(
new Intent(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED),
- UserHandle.OWNER);
+ UserHandle.SYSTEM);
}
@Override
public SystemUpdatePolicy getSystemUpdatePolicy() {
synchronized (this) {
- SystemUpdatePolicy policy = mDeviceOwner.getSystemUpdatePolicy();
+ SystemUpdatePolicy policy = mOwners.getSystemUpdatePolicy();
if (policy != null && !policy.isValid()) {
Slog.w(LOG_TAG, "Stored system update policy is invalid, return null instead.");
return null;
@@ -6356,7 +6395,7 @@
"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" +
+ Slog.w(LOG_TAG, "Only the system update service in the primary user " +
"can broadcast update information.");
return;
}
@@ -6369,6 +6408,7 @@
if (deviceOwnerPackage == null) {
return;
}
+ final UserHandle deviceOwnerUser = new UserHandle(mOwners.getDeviceOwnerUserId());
ActivityInfo[] receivers = null;
try {
@@ -6384,7 +6424,7 @@
if (permission.BIND_DEVICE_ADMIN.equals(receivers[i].permission)) {
intent.setComponent(new ComponentName(deviceOwnerPackage,
receivers[i].name));
- mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+ mContext.sendBroadcastAsUser(intent, deviceOwnerUser);
}
}
} finally {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
similarity index 85%
rename from services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
rename to services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 791d0dd..87cf28f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -26,12 +26,14 @@
import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.util.ArrayMap;
import android.util.AtomicFile;
+import android.util.Log;
import android.util.Slog;
import android.util.Xml;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import org.xmlpull.v1.XmlPullParser;
@@ -44,7 +46,6 @@
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -55,9 +56,11 @@
* Stores and restores state for the Device and Profile owners. By definition there can be
* only one device owner, but there may be a profile owner for each user.
*/
-class DeviceOwner {
+class Owners {
private static final String TAG = "DevicePolicyManagerService";
+ private static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE
+
private static final String DEVICE_OWNER_XML_LEGACY = "device_owner.xml";
private static final String DEVICE_OWNER_XML = "device_owner_2.xml";
@@ -69,6 +72,8 @@
private static final String TAG_DEVICE_OWNER = "device-owner";
private static final String TAG_DEVICE_INITIALIZER = "device-initializer";
private static final String TAG_PROFILE_OWNER = "profile-owner";
+ // Holds "context" for device-owner, this must not be show up before device-owner.
+ private static final String TAG_DEVICE_OWNER_CONTEXT = "device-owner-context";
private static final String ATTR_NAME = "name";
private static final String ATTR_PACKAGE = "package";
@@ -83,16 +88,18 @@
// Internal state for the device owner package.
private OwnerInfo mDeviceOwner;
+ private int mDeviceOwnerUserId = UserHandle.USER_NULL;
+
// Internal state for the device initializer package.
private OwnerInfo mDeviceInitializer;
// Internal state for the profile owner packages.
- private final HashMap<Integer, OwnerInfo> mProfileOwners = new HashMap<Integer, OwnerInfo>();
+ private final ArrayMap<Integer, OwnerInfo> mProfileOwners = new ArrayMap<>();
// Local system update policy controllable by device owner.
private SystemUpdatePolicy mSystemUpdatePolicy;
- public DeviceOwner(Context context) {
+ public Owners(Context context) {
mContext = context;
mUserManager = UserManager.get(mContext);
}
@@ -106,11 +113,18 @@
final File legacy = getLegacyConfigFileWithTestOverride();
if (readLegacyOwnerFile(legacy)) {
+ if (DEBUG) {
+ Log.d(TAG, "Legacy config file found.");
+ }
+
// Legacy file exists, write to new files and remove the legacy one.
writeDeviceOwner();
for (int userId : getProfileOwnerKeys()) {
writeProfileOwner(userId);
}
+ if (DEBUG) {
+ Log.d(TAG, "Deleting legacy config file");
+ }
if (!legacy.delete()) {
Slog.e(TAG, "Failed to remove the legacy setting file");
}
@@ -120,9 +134,9 @@
// No legacy file, read from the new format files.
new DeviceOwnerReadWriter().readFromFileLocked();
- final List<UserInfo> users = mUserManager.getUsers(); // XXX double check this is the correct profile set.
+ final List<UserInfo> users = mUserManager.getUsers();
for (UserInfo ui : users) {
- new ProfileOwnerReadWriter(ui.id).readFromFileLocked(); // XXX double check ID is the right one.
+ new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
}
}
}
@@ -131,16 +145,26 @@
return mDeviceOwner != null ? mDeviceOwner.packageName : null;
}
+ int getDeviceOwnerUserId() {
+ return mDeviceOwnerUserId;
+ }
+
String getDeviceOwnerName() {
return mDeviceOwner != null ? mDeviceOwner.name : null;
}
- void setDeviceOwner(String packageName, String ownerName) {
+ void setDeviceOwner(String packageName, String ownerName, int userId) {
+ if (userId < 0) {
+ Slog.e(TAG, "Invalid user id for device owner user: " + userId);
+ return;
+ }
mDeviceOwner = new OwnerInfo(ownerName, packageName);
+ mDeviceOwnerUserId = userId;
}
void clearDeviceOwner() {
mDeviceOwner = null;
+ mDeviceOwnerUserId = UserHandle.USER_NULL;
}
ComponentName getDeviceInitializerComponent() {
@@ -181,6 +205,11 @@
return profileOwner != null ? profileOwner.name : null;
}
+ String getProfileOwnerPackage(int userId) {
+ OwnerInfo profileOwner = mProfileOwners.get(userId);
+ return profileOwner != null ? profileOwner.packageName : null;
+ }
+
Set<Integer> getProfileOwnerKeys() {
return mProfileOwners.keySet();
}
@@ -201,20 +230,6 @@
return mDeviceOwner != null;
}
- static boolean isInstalled(String packageName, PackageManager pm) {
- try {
- PackageInfo pi;
- if ((pi = pm.getPackageInfo(packageName, 0)) != null) {
- if ((pi.applicationInfo.flags) != 0) {
- return true;
- }
- }
- } catch (NameNotFoundException nnfe) {
- Slog.w(TAG, "Device Owner package " + packageName + " not installed.");
- }
- return false;
- }
-
static boolean isInstalledForUser(String packageName, int userHandle) {
try {
PackageInfo pi = (AppGlobals.getPackageManager())
@@ -249,6 +264,7 @@
String name = parser.getAttributeValue(null, ATTR_NAME);
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
mDeviceOwner = new OwnerInfo(name, packageName);
+ mDeviceOwnerUserId = UserHandle.USER_SYSTEM;
} else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
String initializerComponentStr =
@@ -301,12 +317,18 @@
void writeDeviceOwner() {
synchronized (this) {
+ if (DEBUG) {
+ Log.d(TAG, "Writing to device owner file");
+ }
new DeviceOwnerReadWriter().writeToFileLocked();
}
}
void writeProfileOwner(int userId) {
synchronized (this) {
+ if (DEBUG) {
+ Log.d(TAG, "Writing to profile owner file for user " + userId);
+ }
new ProfileOwnerReadWriter(userId).writeToFileLocked();
}
}
@@ -322,14 +344,23 @@
void writeToFileLocked() {
if (!shouldWrite()) {
+ if (DEBUG) {
+ Log.d(TAG, "No need to write to " + mFile);
+ }
// No contents, remove the file.
if (mFile.exists()) {
+ if (DEBUG) {
+ Log.d(TAG, "Deleting existing " + mFile);
+ }
if (!mFile.delete()) {
Slog.e(TAG, "Failed to remove " + mFile.getPath());
}
}
return;
}
+ if (DEBUG) {
+ Log.d(TAG, "Writing to " + mFile);
+ }
final AtomicFile f = new AtomicFile(mFile);
FileOutputStream outputStream = null;
@@ -364,8 +395,14 @@
void readFromFileLocked() {
if (!mFile.exists()) {
+ if (DEBUG) {
+ Log.d(TAG, "" + mFile + " doesn't exist");
+ }
return;
}
+ if (DEBUG) {
+ Log.d(TAG, "Reading from " + mFile);
+ }
final AtomicFile f = new AtomicFile(mFile);
InputStream input = null;
try {
@@ -393,6 +430,7 @@
Slog.e(TAG, "Invalid root tag: " + tag);
return;
}
+ continue;
}
// readInner() will only see START_TAG at depth >= 2.
if (!readInner(parser, depth, tag)) {
@@ -428,7 +466,11 @@
void writeInner(XmlSerializer out) throws IOException {
if (mDeviceOwner != null) {
mDeviceOwner.writeToXml(out, TAG_DEVICE_OWNER);
+ out.startTag(null, TAG_DEVICE_OWNER_CONTEXT);
+ out.attribute(null, ATTR_USERID, String.valueOf(mDeviceOwnerUserId));
+ out.endTag(null, TAG_DEVICE_OWNER_CONTEXT);
}
+
if (mDeviceInitializer != null) {
mDeviceInitializer.writeToXml(out, TAG_DEVICE_INITIALIZER);
}
@@ -447,7 +489,18 @@
switch (tag) {
case TAG_DEVICE_OWNER:
mDeviceOwner = OwnerInfo.readFromXml(parser);
+ mDeviceOwnerUserId = UserHandle.USER_SYSTEM; // Set default
break;
+ case TAG_DEVICE_OWNER_CONTEXT: {
+ final String userIdString =
+ parser.getAttributeValue(null, ATTR_USERID);
+ try {
+ mDeviceOwnerUserId = Integer.parseInt(userIdString);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Error parsing user-id " + userIdString);
+ }
+ break;
+ }
case TAG_DEVICE_INITIALIZER:
mDeviceInitializer = OwnerInfo.readFromXml(parser);
break;
@@ -558,7 +611,6 @@
pw.println(prefix + "admin=" + admin);
pw.println(prefix + "name=" + name);
pw.println(prefix + "package=" + packageName);
- pw.println();
}
}
@@ -566,6 +618,17 @@
if (mDeviceOwner != null) {
pw.println(prefix + "Device Owner: ");
mDeviceOwner.dump(prefix + " ", pw);
+ pw.println(prefix + " User ID: " + mDeviceOwnerUserId);
+ pw.println();
+ }
+ if (mDeviceInitializer != null) {
+ pw.println(prefix + "Device Initializer: ");
+ mDeviceInitializer.dump(prefix + " ", pw);
+ pw.println();
+ }
+ if (mSystemUpdatePolicy != null) {
+ pw.println(prefix + "System Update Policy: " + mSystemUpdatePolicy);
+ pw.println();
}
if (mProfileOwners != null) {
for (Map.Entry<Integer, OwnerInfo> entry : mProfileOwners.entrySet()) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 21dd647b..93dc6cb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -38,6 +38,7 @@
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.IMountService;
import android.util.DisplayMetrics;
@@ -175,97 +176,103 @@
}
private void run() {
- // If a device's clock is before 1970 (before 0), a lot of
- // APIs crash dealing with negative numbers, notably
- // java.io.File#setLastModified, so instead we fake it and
- // hope that time from cell towers or NTP fixes it shortly.
- if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
- Slog.w(TAG, "System clock is before 1970; setting to 1970.");
- SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
- }
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "InitBeforeStartServices");
+ // If a device's clock is before 1970 (before 0), a lot of
+ // APIs crash dealing with negative numbers, notably
+ // java.io.File#setLastModified, so instead we fake it and
+ // hope that time from cell towers or NTP fixes it shortly.
+ if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
+ Slog.w(TAG, "System clock is before 1970; setting to 1970.");
+ SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
+ }
- // If the system has "persist.sys.language" and friends set, replace them with
- // "persist.sys.locale". Note that the default locale at this point is calculated
- // using the "-Duser.locale" command line flag. That flag is usually populated by
- // AndroidRuntime using the same set of system properties, but only the system_server
- // and system apps are allowed to set them.
- //
- // NOTE: Most changes made here will need an equivalent change to
- // core/jni/AndroidRuntime.cpp
- if (!SystemProperties.get("persist.sys.language").isEmpty()) {
- final String languageTag = Locale.getDefault().toLanguageTag();
+ // If the system has "persist.sys.language" and friends set, replace them with
+ // "persist.sys.locale". Note that the default locale at this point is calculated
+ // using the "-Duser.locale" command line flag. That flag is usually populated by
+ // AndroidRuntime using the same set of system properties, but only the system_server
+ // and system apps are allowed to set them.
+ //
+ // NOTE: Most changes made here will need an equivalent change to
+ // core/jni/AndroidRuntime.cpp
+ if (!SystemProperties.get("persist.sys.language").isEmpty()) {
+ final String languageTag = Locale.getDefault().toLanguageTag();
- SystemProperties.set("persist.sys.locale", languageTag);
- SystemProperties.set("persist.sys.language", "");
- SystemProperties.set("persist.sys.country", "");
- SystemProperties.set("persist.sys.localevar", "");
- }
+ SystemProperties.set("persist.sys.locale", languageTag);
+ SystemProperties.set("persist.sys.language", "");
+ SystemProperties.set("persist.sys.country", "");
+ SystemProperties.set("persist.sys.localevar", "");
+ }
- // Here we go!
- Slog.i(TAG, "Entered the Android system server!");
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis());
+ // Here we go!
+ Slog.i(TAG, "Entered the Android system server!");
+ EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis());
- // In case the runtime switched since last boot (such as when
- // the old runtime was removed in an OTA), set the system
- // property so that it is in sync. We can't do this in
- // libnativehelper's JniInvocation::Init code where we already
- // had to fallback to a different runtime because it is
- // running as root and we need to be the system user to set
- // the property. http://b/11463182
- SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());
+ // In case the runtime switched since last boot (such as when
+ // the old runtime was removed in an OTA), set the system
+ // property so that it is in sync. We can't do this in
+ // libnativehelper's JniInvocation::Init code where we already
+ // had to fallback to a different runtime because it is
+ // running as root and we need to be the system user to set
+ // the property. http://b/11463182
+ SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());
- // Enable the sampling profiler.
- if (SamplingProfilerIntegration.isEnabled()) {
- SamplingProfilerIntegration.start();
- mProfilerSnapshotTimer = new Timer();
- mProfilerSnapshotTimer.schedule(new TimerTask() {
- @Override
- public void run() {
- SamplingProfilerIntegration.writeSnapshot("system_server", null);
- }
- }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
- }
+ // Enable the sampling profiler.
+ if (SamplingProfilerIntegration.isEnabled()) {
+ SamplingProfilerIntegration.start();
+ mProfilerSnapshotTimer = new Timer();
+ mProfilerSnapshotTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ SamplingProfilerIntegration.writeSnapshot("system_server", null);
+ }
+ }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
+ }
- // Mmmmmm... more memory!
- VMRuntime.getRuntime().clearGrowthLimit();
+ // Mmmmmm... more memory!
+ VMRuntime.getRuntime().clearGrowthLimit();
- // The system server has to run all of the time, so it needs to be
- // as efficient as possible with its memory usage.
- VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
+ // The system server has to run all of the time, so it needs to be
+ // as efficient as possible with its memory usage.
+ VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
- // Some devices rely on runtime fingerprint generation, so make sure
- // we've defined it before booting further.
- Build.ensureFingerprintProperty();
+ // Some devices rely on runtime fingerprint generation, so make sure
+ // we've defined it before booting further.
+ Build.ensureFingerprintProperty();
- // Within the system server, it is an error to access Environment paths without
- // explicitly specifying a user.
- Environment.setUserRequired(true);
+ // Within the system server, it is an error to access Environment paths without
+ // explicitly specifying a user.
+ Environment.setUserRequired(true);
- // Ensure binder calls into the system always run at foreground priority.
- BinderInternal.disableBackgroundScheduling(true);
+ // Ensure binder calls into the system always run at foreground priority.
+ BinderInternal.disableBackgroundScheduling(true);
- // Prepare the main looper thread (this thread).
- android.os.Process.setThreadPriority(
+ // Prepare the main looper thread (this thread).
+ android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
- android.os.Process.setCanSelfBackground(false);
- Looper.prepareMainLooper();
+ android.os.Process.setCanSelfBackground(false);
+ Looper.prepareMainLooper();
- // Initialize native services.
- System.loadLibrary("android_servers");
+ // Initialize native services.
+ System.loadLibrary("android_servers");
- // Check whether we failed to shut down last time we tried.
- // This call may not return.
- performPendingShutdown();
+ // Check whether we failed to shut down last time we tried.
+ // This call may not return.
+ performPendingShutdown();
- // Initialize the system context.
- createSystemContext();
+ // Initialize the system context.
+ createSystemContext();
- // Create the system service manager.
- mSystemServiceManager = new SystemServiceManager(mSystemContext);
- LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
+ // Create the system service manager.
+ mSystemServiceManager = new SystemServiceManager(mSystemContext);
+ LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
// Start services.
try {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartServices");
startBootstrapServices();
startCoreServices();
startOtherServices();
@@ -273,6 +280,8 @@
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
// For debug builds, log event loop stalls to dropbox for analysis.
@@ -340,7 +349,9 @@
// Now that the power manager has been started, let the activity manager
// initialize power management features.
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "InitPowerManagement");
mActivityManagerService.initPowerManagement();
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
// Manages LEDs and display backlight so we need it to bring up the display.
mSystemServiceManager.startService(LightsService.class);
@@ -363,14 +374,16 @@
}
// Start the package manager.
- Slog.i(TAG, "Package Manager");
+ traceBeginAndSlog("StartPackageManagerService");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Slog.i(TAG, "User Service");
+ traceBeginAndSlog("StartUserManagerService");
ServiceManager.addService(Context.USER_SERVICE, UserManagerService.getInstance());
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
// Initialize attribute cache used to cache resources from packages.
AttributeCache.init(mSystemContext);
@@ -442,17 +455,20 @@
Slog.i(TAG, "Reading configuration...");
SystemConfig.getInstance();
- Slog.i(TAG, "Scheduling Policy");
+ traceBeginAndSlog("StartSchedulingPolicyService");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mSystemServiceManager.startService(TelecomLoaderService.class);
- Slog.i(TAG, "Telephony Registry");
+ traceBeginAndSlog("StartTelephonyRegistry");
telephonyRegistry = new TelephonyRegistry(context);
ServiceManager.addService("telephony.registry", telephonyRegistry);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Slog.i(TAG, "Entropy Mixer");
+ traceBeginAndSlog("StartEntropyMixer");
entropyMixer = new EntropyMixer(context);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mContentResolver = context.getContentResolver();
@@ -460,47 +476,55 @@
mSystemServiceManager.startService(CameraService.class);
// The AccountManager must come before the ContentService
+ traceBeginAndSlog("StartAccountManagerService");
try {
// TODO: seems like this should be disable-able, but req'd by ContentService
- Slog.i(TAG, "Account Manager");
accountManager = new AccountManagerService(context);
ServiceManager.addService(Context.ACCOUNT_SERVICE, accountManager);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Account Manager", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Slog.i(TAG, "Content Manager");
+ traceBeginAndSlog("StartContentService");
contentService = ContentService.main(context,
mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Slog.i(TAG, "System Content Providers");
+ traceBeginAndSlog("InstallSystemProviders");
mActivityManagerService.installSystemProviders();
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Slog.i(TAG, "Vibrator Service");
+ traceBeginAndSlog("StartVibratorService");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Slog.i(TAG, "Consumer IR Service");
+ traceBeginAndSlog("StartConsumerIrService");
consumerIr = new ConsumerIrService(context);
ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mSystemServiceManager.startService(AlarmManagerService.class);
alarm = IAlarmManager.Stub.asInterface(
ServiceManager.getService(Context.ALARM_SERVICE));
- Slog.i(TAG, "Init Watchdog");
+ traceBeginAndSlog("InitWatchdog");
final Watchdog watchdog = Watchdog.getInstance();
watchdog.init(context, mActivityManagerService);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Slog.i(TAG, "Input Manager");
+ traceBeginAndSlog("StartInputManagerService");
inputManager = new InputManagerService(context);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Slog.i(TAG, "Window Manager");
+ traceBeginAndSlog("StartWindowManagerService");
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mActivityManagerService.setWindowManager(wm);
@@ -523,7 +547,6 @@
} else if (disableBluetooth) {
Slog.i(TAG, "Bluetooth Service disabled by config");
} else {
- Slog.i(TAG, "Bluetooth Service");
mSystemServiceManager.startService(BluetoothService.class);
}
} catch (RuntimeException e) {
@@ -544,21 +567,23 @@
// Bring up services needed for UI.
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+ traceBeginAndSlog("StartInputMethodManagerService");
try {
- Slog.i(TAG, "Input Method Service");
imm = new InputMethodManagerService(context);
ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
} catch (Throwable e) {
reportWtf("starting Input Manager Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ traceBeginAndSlog("StartAccessibilityManagerService");
try {
- Slog.i(TAG, "Accessibility Manager");
ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
new AccessibilityManagerService(context));
} catch (Throwable e) {
reportWtf("starting Accessibility Manager", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
try {
@@ -588,11 +613,13 @@
// as appropriate.
mSystemServiceManager.startService(UiModeManagerService.class);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PerformBootDexOpt");
try {
mPackageManagerService.performBootDexOpt();
} catch (Throwable e) {
reportWtf("performing boot dexopt", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
try {
ActivityManagerNative.getDefault().showBootMessage(
@@ -604,13 +631,14 @@
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!disableNonCoreServices) {
+ traceBeginAndSlog("StartLockSettingsService");
try {
- Slog.i(TAG, "LockSettingsService");
lockSettings = new LockSettingsService(context);
ServiceManager.addService("lock_settings", lockSettings);
} catch (Throwable e) {
reportWtf("starting LockSettingsService service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (!SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("")) {
mSystemServiceManager.startService(PersistentDataBlockService.class);
@@ -624,64 +652,70 @@
}
if (!disableSystemUI) {
+ traceBeginAndSlog("StartStatusBarManagerService");
try {
- Slog.i(TAG, "Status Bar");
statusBar = new StatusBarManagerService(context, wm);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
reportWtf("starting StatusBarManagerService", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
if (!disableNonCoreServices) {
+ traceBeginAndSlog("StartClipboardService");
try {
- Slog.i(TAG, "Clipboard Service");
ServiceManager.addService(Context.CLIPBOARD_SERVICE,
new ClipboardService(context));
} catch (Throwable e) {
reportWtf("starting Clipboard Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
if (!disableNetwork) {
+ traceBeginAndSlog("StartNetworkManagementService");
try {
- Slog.i(TAG, "NetworkManagement Service");
networkManagement = NetworkManagementService.create(context);
ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
} catch (Throwable e) {
reportWtf("starting NetworkManagement Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
if (!disableNonCoreServices) {
+ traceBeginAndSlog("StartTextServicesManagerService");
try {
- Slog.i(TAG, "Text Service Manager Service");
tsms = new TextServicesManagerService(context);
ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
} catch (Throwable e) {
reportWtf("starting Text Service Manager Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
if (!disableNetwork) {
+ traceBeginAndSlog("StartNetworkScoreService");
try {
- Slog.i(TAG, "Network Score Service");
networkScore = new NetworkScoreService(context);
ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
} catch (Throwable e) {
reportWtf("starting Network Score Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ traceBeginAndSlog("StartNetworkStatsService");
try {
- Slog.i(TAG, "NetworkStats Service");
networkStats = new NetworkStatsService(context, networkManagement, alarm);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
} catch (Throwable e) {
reportWtf("starting NetworkStats Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ traceBeginAndSlog("StartNetworkPolicyManagerService");
try {
- Slog.i(TAG, "NetworkPolicy Service");
networkPolicy = new NetworkPolicyManagerService(
context, mActivityManagerService,
(IPowerManager)ServiceManager.getService(Context.POWER_SERVICE),
@@ -690,6 +724,7 @@
} catch (Throwable e) {
reportWtf("starting NetworkPolicy Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
@@ -703,8 +738,8 @@
mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
}
+ traceBeginAndSlog("StartConnectivityService");
try {
- Slog.i(TAG, "Connectivity Service");
connectivity = new ConnectivityService(
context, networkManagement, networkStats, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
@@ -713,25 +748,28 @@
} catch (Throwable e) {
reportWtf("starting Connectivity Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ traceBeginAndSlog("StartNsdService");
try {
- Slog.i(TAG, "Network Service Discovery Service");
serviceDiscovery = NsdService.create(context);
ServiceManager.addService(
Context.NSD_SERVICE, serviceDiscovery);
} catch (Throwable e) {
reportWtf("starting Service Discovery Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
if (!disableNonCoreServices) {
+ traceBeginAndSlog("StartUpdateLockService");
try {
- Slog.i(TAG, "UpdateLock Service");
ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
new UpdateLockService(context));
} catch (Throwable e) {
reportWtf("starting UpdateLockService", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
/*
@@ -740,25 +778,31 @@
* first before continuing.
*/
if (mountService != null && !mOnlyCore) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "WaitForAsecScan");
try {
mountService.waitForAsecScan();
} catch (RemoteException ignored) {
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeAccountManagerServiceReady");
try {
if (accountManager != null)
accountManager.systemReady();
} catch (Throwable e) {
reportWtf("making Account Manager Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeContentServiceReady");
try {
if (contentService != null)
contentService.systemReady();
} catch (Throwable e) {
reportWtf("making Content Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mSystemServiceManager.startService(NotificationManagerService.class);
notification = INotificationManager.Stub.asInterface(
@@ -768,66 +812,72 @@
mSystemServiceManager.startService(DeviceStorageMonitorService.class);
if (!disableLocation) {
+ traceBeginAndSlog("StartLocationManagerService");
try {
- Slog.i(TAG, "Location Manager");
location = new LocationManagerService(context);
ServiceManager.addService(Context.LOCATION_SERVICE, location);
} catch (Throwable e) {
reportWtf("starting Location Manager", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ traceBeginAndSlog("StartCountryDetectorService");
try {
- Slog.i(TAG, "Country Detector");
countryDetector = new CountryDetectorService(context);
ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
} catch (Throwable e) {
reportWtf("starting Country Detector", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
if (!disableNonCoreServices) {
+ traceBeginAndSlog("StartSearchManagerService");
try {
- Slog.i(TAG, "Search Service");
ServiceManager.addService(Context.SEARCH_SERVICE,
new SearchManagerService(context));
} catch (Throwable e) {
reportWtf("starting Search Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
mSystemServiceManager.startService(DropBoxManagerService.class);
if (!disableNonCoreServices && context.getResources().getBoolean(
R.bool.config_enableWallpaperService)) {
+ traceBeginAndSlog("StartWallpaperManagerService");
try {
- Slog.i(TAG, "Wallpaper Service");
wallpaper = new WallpaperManagerService(context);
ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
} catch (Throwable e) {
reportWtf("starting Wallpaper Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
+ traceBeginAndSlog("StartAudioService");
try {
- Slog.i(TAG, "Audio Service");
audioService = new AudioService(context);
ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
} catch (Throwable e) {
reportWtf("starting Audio Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (!disableNonCoreServices) {
mSystemServiceManager.startService(DockObserver.class);
}
+ traceBeginAndSlog("StartWiredAccessoryManager");
try {
- Slog.i(TAG, "Wired Accessory Manager");
// Listen for wired headset changes
inputManager.setWiredAccessoryCallbacks(
new WiredAccessoryManager(context, inputManager));
} catch (Throwable e) {
reportWtf("starting WiredAccessoryManager", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (!disableNonCoreServices) {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) {
@@ -839,17 +889,20 @@
|| mPackageManager.hasSystemFeature(
PackageManager.FEATURE_USB_ACCESSORY)) {
// Manage USB host and device support
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartUsbService");
mSystemServiceManager.startService(USB_SERVICE_CLASS);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
+ traceBeginAndSlog("StartSerialService");
try {
- Slog.i(TAG, "Serial Service");
// Serial port support
serial = new SerialService(context);
ServiceManager.addService(Context.SERIAL_SERVICE, serial);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting SerialService", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
mSystemServiceManager.startService(TwilightService.class);
@@ -875,49 +928,54 @@
}
}
+ traceBeginAndSlog("StartDiskStatsService");
try {
- Slog.i(TAG, "DiskStats Service");
ServiceManager.addService("diskstats", new DiskStatsService(context));
} catch (Throwable e) {
reportWtf("starting DiskStats Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ traceBeginAndSlog("StartSamplingProfilerService");
try {
// need to add this service even if SamplingProfilerIntegration.isEnabled()
// is false, because it is this service that detects system property change and
// turns on SamplingProfilerIntegration. Plus, when sampling profiler doesn't work,
// there is little overhead for running this service.
- Slog.i(TAG, "SamplingProfiler Service");
ServiceManager.addService("samplingprofiler",
new SamplingProfilerService(context));
} catch (Throwable e) {
reportWtf("starting SamplingProfiler Service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (!disableNetwork && !disableNetworkTime) {
+ traceBeginAndSlog("StartNetworkTimeUpdateService");
try {
- Slog.i(TAG, "NetworkTimeUpdateService");
networkTimeUpdater = new NetworkTimeUpdateService(context);
} catch (Throwable e) {
reportWtf("starting NetworkTimeUpdate service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
+ traceBeginAndSlog("StartCommonTimeManagementService");
try {
- Slog.i(TAG, "CommonTimeManagementService");
commonTimeMgmtService = new CommonTimeManagementService(context);
ServiceManager.addService("commontime_management", commonTimeMgmtService);
} catch (Throwable e) {
reportWtf("starting CommonTimeManagementService service", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (!disableNetwork) {
+ traceBeginAndSlog("CertBlacklister");
try {
- Slog.i(TAG, "CertBlacklister");
CertBlacklister blacklister = new CertBlacklister(context);
} catch (Throwable e) {
reportWtf("starting CertBlacklister", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
if (!disableNonCoreServices) {
@@ -926,13 +984,14 @@
}
if (!disableNonCoreServices && ZygoteInit.PRELOAD_RESOURCES) {
+ traceBeginAndSlog("StartAssetAtlasService");
try {
- Slog.i(TAG, "Assets Atlas Service");
atlas = new AssetAtlasService(context);
ServiceManager.addService(AssetAtlasService.ASSET_ATLAS_SERVICE, atlas);
} catch (Throwable e) {
reportWtf("starting AssetAtlasService", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
if (!disableNonCoreServices) {
@@ -957,24 +1016,26 @@
}
if (!disableNonCoreServices) {
+ traceBeginAndSlog("StartMediaRouterService");
try {
- Slog.i(TAG, "Media Router Service");
mediaRouter = new MediaRouterService(context);
ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter);
} catch (Throwable e) {
reportWtf("starting MediaRouterService", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mSystemServiceManager.startService(TrustManagerService.class);
mSystemServiceManager.startService(FingerprintService.class);
+ traceBeginAndSlog("StartBackgroundDexOptService");
try {
- Slog.i(TAG, "BackgroundDexOptService");
BackgroundDexOptService.schedule(context, 0);
} catch (Throwable e) {
reportWtf("starting BackgroundDexOptService", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
@@ -1002,12 +1063,15 @@
// It is now time to start up the app processes...
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeVibratorServiceReady");
try {
vibrator.systemReady();
} catch (Throwable e) {
reportWtf("making Vibrator Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeLockSettingsServiceReady");
if (lockSettings != null) {
try {
lockSettings.systemReady();
@@ -1015,17 +1079,20 @@
reportWtf("making Lock Settings Service ready", e);
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
// Needed by DevicePolicyManager for initialization
mSystemServiceManager.startBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
mSystemServiceManager.startBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeWindowManagerServiceReady");
try {
wm.systemReady();
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (safeMode) {
mActivityManagerService.showSafeModeOverlay();
@@ -1046,25 +1113,32 @@
systemTheme.rebase();
}
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakePowerManagerServiceReady");
try {
// TODO: use boot phase
mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
} catch (Throwable e) {
reportWtf("making Power Manager Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakePackageManagerServiceReady");
try {
mPackageManagerService.systemReady();
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeDisplayManagerServiceReady");
try {
// TODO: use boot phase and communicate these flags some other way
mDisplayManagerService.systemReady(safeMode, mOnlyCore);
} catch (Throwable e) {
reportWtf("making Display Manager Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
// These are needed to propagate to the runnable below.
final NetworkManagementService networkManagementF = networkManagement;
@@ -1098,55 +1172,76 @@
Slog.i(TAG, "Making services ready");
mSystemServiceManager.startBootPhase(
SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PhaseActivityManagerReady");
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartObservingNativeCrashes");
try {
mActivityManagerService.startObservingNativeCrashes();
} catch (Throwable e) {
reportWtf("observing native crashes", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
Slog.i(TAG, "WebViewFactory preparation");
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "WebViewFactoryPreparation");
WebViewFactory.prepareWebViewInSystemServer();
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartSystemUI");
try {
startSystemUi(context);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeMountServiceReady");
try {
if (networkScoreF != null) networkScoreF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Score Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeNetworkManagementServiceReady");
try {
if (networkManagementF != null) networkManagementF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Managment Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeNetworkStatsServiceReady");
try {
if (networkStatsF != null) networkStatsF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Stats Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeNetworkPolicyServiceReady");
try {
if (networkPolicyF != null) networkPolicyF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Policy Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeConnectivityServiceReady");
try {
if (connectivityF != null) connectivityF.systemReady();
} catch (Throwable e) {
reportWtf("making Connectivity Service ready", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeAudioServiceReady");
try {
if (audioServiceF != null) audioServiceF.systemReady();
} catch (Throwable e) {
reportWtf("Notifying AudioService running", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
Watchdog.getInstance().start();
// It is now okay to let the various system services start their
// third party code...
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PhaseThirdPartyAppsCanStart");
mSystemServiceManager.startBootPhase(
SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
@@ -1215,6 +1310,7 @@
} catch (Throwable e) {
reportWtf("Notifying MmsService running", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
});
}
@@ -1226,4 +1322,9 @@
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
}
-}
+
+ private static void traceBeginAndSlog(String name) {
+ 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 3ecfd55..f6de12d 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -27,6 +27,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.XmlResourceParser;
+import android.media.midi.IBluetoothMidiService;
import android.media.midi.IMidiDeviceListener;
import android.media.midi.IMidiDeviceOpenCallback;
import android.media.midi.IMidiDeviceServer;
@@ -394,7 +395,20 @@
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- IMidiDeviceServer server = IMidiDeviceServer.Stub.asInterface(service);
+ IMidiDeviceServer server = null;
+ if (mBluetoothDevice != null) {
+ IBluetoothMidiService mBluetoothMidiService = IBluetoothMidiService.Stub.asInterface(service);
+ try {
+ // We need to explicitly add the device in a separate method
+ // because onBind() is only called once.
+ IBinder deviceBinder = mBluetoothMidiService.addBluetoothDevice(mBluetoothDevice);
+ server = IMidiDeviceServer.Stub.asInterface(deviceBinder);
+ } catch(RemoteException e) {
+ Log.e(TAG, "Could not call addBluetoothDevice()", e);
+ }
+ } else {
+ server = IMidiDeviceServer.Stub.asInterface(service);
+ }
setDeviceServer(server);
}
@@ -411,7 +425,6 @@
intent.setComponent(new ComponentName(
MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE,
MidiManager.BLUETOOTH_MIDI_SERVICE_CLASS));
- intent.putExtra("device", mBluetoothDevice);
} else {
intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
intent.setComponent(
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index a91ddb8..cbf8fc2 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -55,6 +55,7 @@
public static final int MIN_PACKET_LENGTH_L3 = MIN_PACKET_LENGTH_BOOTP + 20 + 8;
public static final int MIN_PACKET_LENGTH_L2 = MIN_PACKET_LENGTH_L3 + 14;
+ public static final int HWADDR_LEN = 16;
public static final int MAX_OPTION_LEN = 255;
/**
* IP layer definitions.
@@ -399,7 +400,7 @@
buf.put(mRelayIp.getAddress());
buf.put(mClientMac);
buf.position(buf.position() +
- (16 - mClientMac.length) // pad addr to 16 bytes
+ (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes
+ 64 // empty server host name (64 bytes)
+ 128); // empty boot file name (128 bytes)
buf.putInt(0x63825363); // magic number
@@ -786,7 +787,7 @@
byte type = packet.get();
byte hwType = packet.get();
- byte addrLen = packet.get();
+ int addrLen = packet.get() & 0xff;
byte hops = packet.get();
transactionId = packet.getInt();
secs = packet.getShort();
@@ -807,6 +808,16 @@
return null;
}
+ // Some DHCP servers have been known to announce invalid client hardware address values such
+ // as 0xff. The legacy DHCP client accepted these becuause it does not check the length at
+ // all but only checks that the interface MAC address matches the first bytes of the address
+ // in the packets. We're a bit stricter: if the length is obviously invalid (i.e., bigger
+ // than the size of the field), we fudge it to 6 (Ethernet). http://b/23725795
+ // TODO: evaluate whether to make this test more liberal.
+ if (addrLen > HWADDR_LEN) {
+ addrLen = ETHER_BROADCAST.length;
+ }
+
clientMac = new byte[addrLen];
packet.get(clientMac);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 386a9cb..7d1282b 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -39,6 +39,7 @@
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/assets/OwnersTest/test01/input.xml b/services/tests/servicestests/assets/OwnersTest/test01/input.xml
new file mode 100644
index 0000000..db3e974
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/test01/input.xml
@@ -0,0 +1 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
diff --git a/services/tests/servicestests/assets/OwnersTest/test02/input.xml b/services/tests/servicestests/assets/OwnersTest/test02/input.xml
new file mode 100644
index 0000000..321842b
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/test02/input.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<device-owner package="com.google.android.testdpc" />
diff --git a/services/tests/servicestests/assets/OwnersTest/test03/input.xml b/services/tests/servicestests/assets/OwnersTest/test03/input.xml
new file mode 100644
index 0000000..1bbfdadf
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/test03/input.xml
@@ -0,0 +1,3 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<profile-owner package="com.google.android.testdpc0" name="0" userId="10" component="com.google.android.testdpc/com.google.android.testdpc.DeviceAdminReceiver0" />
+<profile-owner package="com.google.android.testdpc1" name="1" userId="11" />
diff --git a/services/tests/servicestests/assets/OwnersTest/test04/input.xml b/services/tests/servicestests/assets/OwnersTest/test04/input.xml
new file mode 100644
index 0000000..8be51d9
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/test04/input.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<device-owner package="com.google.android.testdpc" />
+<profile-owner package="com.google.android.testdpc0" name="0" userId="10" component="com.google.android.testdpc/com.google.android.testdpc.DeviceAdminReceiver0" />
+<profile-owner package="com.google.android.testdpc1" name="1" userId="11" />
+<device-initializer package="com.google.android.testdpcx" name="di" component="com.google.android.testdpcx/receiver" />
+<system-update-policy policy_type="5" />
diff --git a/services/tests/servicestests/assets/OwnersTest/test05/input.xml b/services/tests/servicestests/assets/OwnersTest/test05/input.xml
new file mode 100644
index 0000000..dbcb858
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/test05/input.xml
@@ -0,0 +1,3 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<device-initializer package="com.google.android.testdpcx" name="di" component="com.google.android.testdpcx/receiver" />
+
diff --git a/services/tests/servicestests/assets/OwnersTest/test06/input.xml b/services/tests/servicestests/assets/OwnersTest/test06/input.xml
new file mode 100644
index 0000000..794622b
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/test06/input.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<system-update-policy policy_type="5" />
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index da1df1a..cd3b8bb 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -333,6 +333,76 @@
}
@SmallTest
+ public void testBadHwaddrLength() throws Exception {
+ final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ // IP header.
+ "450001518d0600004011144dc0a82b01c0a82bf7" +
+ // UDP header.
+ "00430044013d9ac7" +
+ // BOOTP header.
+ "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
+ // MAC address.
+ "30766ff2a90c00000000000000000000" +
+ // Server name.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Options
+ "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
+ "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"
+ ).toCharArray(), false));
+ String expectedClientMac = "30766FF2A90C";
+
+ final int hwAddrLenOffset = 20 + 8 + 2;
+ assertEquals(6, packet.get(hwAddrLenOffset));
+
+ // Expect the expected.
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ assertNotNull(offerPacket);
+ assertEquals(6, offerPacket.getClientMac().length);
+ assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
+
+ // Reduce the hardware address length and verify that it shortens the client MAC.
+ packet.flip();
+ packet.put(hwAddrLenOffset, (byte) 5);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ assertNotNull(offerPacket);
+ assertEquals(5, offerPacket.getClientMac().length);
+ assertEquals(expectedClientMac.substring(0, 10),
+ HexDump.toHexString(offerPacket.getClientMac()));
+
+ packet.flip();
+ packet.put(hwAddrLenOffset, (byte) 3);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ assertNotNull(offerPacket);
+ assertEquals(3, offerPacket.getClientMac().length);
+ assertEquals(expectedClientMac.substring(0, 6),
+ HexDump.toHexString(offerPacket.getClientMac()));
+
+ // Set the the hardware address length to 0xff and verify that we a) don't treat it as -1
+ // and crash, and b) hardcode it to 6.
+ packet.flip();
+ packet.put(hwAddrLenOffset, (byte) -1);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ assertNotNull(offerPacket);
+ assertEquals(6, offerPacket.getClientMac().length);
+ assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
+
+ // Set the the hardware address length to a positive invalid value (> 16) and verify that we
+ // hardcode it to 6.
+ packet.flip();
+ packet.put(hwAddrLenOffset, (byte) 17);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ assertNotNull(offerPacket);
+ assertEquals(6, offerPacket.getClientMac().length);
+ assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
+ }
+
+ @SmallTest
public void testPadAndOverloadedOptionsOffer() throws Exception {
// A packet observed in the real world that is interesting for two reasons:
//
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
index fd9fc98..51e14d3 100644
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
@@ -54,7 +54,7 @@
* Timeout in which we are waiting for the system to start the mock
* accessibility services.
*/
- private static final long TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES = 300;
+ private static final long TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES = 1000;
/**
* Timeout used for testing that a service is notified only upon a
@@ -68,6 +68,12 @@
private IAccessibilityManager mManagerService;
@Override
+ protected void setUp() throws Exception {
+ // Reset the state.
+ ensureOnlyMockServicesEnabled(getContext(), false, false);
+ }
+
+ @Override
public void setContext(Context context) {
super.setContext(context);
if (MyFirstMockAccessibilityService.sComponentName == null) {
@@ -92,6 +98,9 @@
@LargeTest
public void testAddClient_AccessibilityDisabledThenEnabled() throws Exception {
+ // at least some service must be enabled, otherwise accessibility will always be disabled.
+ ensureOnlyMockServicesEnabled(mContext, true, false);
+
// make sure accessibility is disabled
ensureAccessibilityEnabled(mContext, false);
@@ -99,7 +108,8 @@
MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
// invoke the method under test
- final int stateFlagsDisabled = mManagerService.addClient(mockClient, UserHandle.USER_OWNER);
+ final int stateFlagsDisabled =
+ mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
boolean enabledAccessibilityDisabled =
(stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -111,11 +121,11 @@
ensureAccessibilityEnabled(mContext, true);
// invoke the method under test
- final int stateFlagsEnabled = mManagerService.addClient(mockClient, UserHandle.USER_OWNER);
+ final int stateFlagsEnabled =
+ mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
boolean enabledAccessibilityEnabled =
(stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
-
// check expected result
assertTrue("The client must be enabled since accessibility is enabled.",
enabledAccessibilityEnabled);
@@ -123,6 +133,9 @@
@LargeTest
public void testAddClient_AccessibilityEnabledThenDisabled() throws Exception {
+ // at least some service must be enabled, otherwise accessibility will always be disabled.
+ ensureOnlyMockServicesEnabled(mContext, true, false);
+
// enable accessibility before registering the client
ensureAccessibilityEnabled(mContext, true);
@@ -130,7 +143,8 @@
MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
// invoke the method under test
- final int stateFlagsEnabled = mManagerService.addClient(mockClient, UserHandle.USER_OWNER);
+ final int stateFlagsEnabled =
+ mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
boolean enabledAccessibilityEnabled =
(stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -142,7 +156,8 @@
ensureAccessibilityEnabled(mContext, false);
// invoke the method under test
- final int stateFlagsDisabled = mManagerService.addClient(mockClient, UserHandle.USER_OWNER);
+ final int stateFlagsDisabled =
+ mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
boolean enabledAccessibilityDisabled =
(stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -162,7 +177,7 @@
// look for the two mock services
for (AccessibilityServiceInfo info : mManagerService.getInstalledAccessibilityServiceList(
- UserHandle.USER_OWNER)) {
+ UserHandle.USER_CURRENT)) {
ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
if (packageName.equals(serviceInfo.packageName)) {
if (firstMockServiceClassName.equals(serviceInfo.name)) {
@@ -181,12 +196,12 @@
@LargeTest
public void testSendAccessibilityEvent_OneService_MatchingPackageAndEventType()
throws Exception {
- // set the accessibility setting value
- ensureAccessibilityEnabled(mContext, true);
-
// enable the mock accessibility service
ensureOnlyMockServicesEnabled(mContext, true, false);
+ // set the accessibility setting value
+ ensureAccessibilityEnabled(mContext, true);
+
// configure the mock service
MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
@@ -203,7 +218,7 @@
service.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(service);
@@ -211,12 +226,12 @@
@LargeTest
public void testSendAccessibilityEvent_OneService_NotMatchingPackage() throws Exception {
- // set the accessibility setting value
- ensureAccessibilityEnabled(mContext, true);
-
// enable the mock accessibility service
ensureOnlyMockServicesEnabled(mContext, true, false);
+ // set the accessibility setting value
+ ensureAccessibilityEnabled(mContext, true);
+
// configure the mock service
MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
@@ -233,7 +248,7 @@
service.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(service);
@@ -241,12 +256,12 @@
@LargeTest
public void testSendAccessibilityEvent_OneService_NotMatchingEventType() throws Exception {
- // set the accessibility setting value
- ensureAccessibilityEnabled(mContext, true);
-
// enable the mock accessibility service
ensureOnlyMockServicesEnabled(mContext, true, false);
+ // set the accessibility setting value
+ ensureAccessibilityEnabled(mContext, true);
+
// configure the mock service
MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
@@ -263,7 +278,7 @@
service.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(service);
@@ -271,12 +286,12 @@
@LargeTest
public void testSendAccessibilityEvent_OneService_NotifivationAfterTimeout() throws Exception {
- // set the accessibility setting value
- ensureAccessibilityEnabled(mContext, true);
-
// enable the mock accessibility service
ensureOnlyMockServicesEnabled(mContext, true, false);
+ // set the accessibility setting value
+ ensureAccessibilityEnabled(mContext, true);
+
// configure the mock service
MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
AccessibilityServiceInfo info = MockAccessibilityService.createDefaultInfo();
@@ -299,8 +314,8 @@
service.replay();
// send the events
- mManagerService.sendAccessibilityEvent(firstEvent, UserHandle.USER_OWNER);
- mManagerService.sendAccessibilityEvent(secondEvent, UserHandle.USER_OWNER);
+ mManagerService.sendAccessibilityEvent(firstEvent, UserHandle.USER_CURRENT);
+ mManagerService.sendAccessibilityEvent(secondEvent, UserHandle.USER_CURRENT);
// wait for #sendAccessibilityEvent to reach the backing service
Thread.sleep(TIMEOUT_BINDER_CALL);
@@ -322,12 +337,12 @@
@LargeTest
public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_DiffFeedback()
throws Exception {
- // set the accessibility setting value
- ensureAccessibilityEnabled(mContext, true);
-
// enable the mock accessibility services
ensureOnlyMockServicesEnabled(mContext, true, true);
+ // set the accessibility setting value
+ ensureAccessibilityEnabled(mContext, true);
+
// configure the first mock service
MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
AccessibilityServiceInfo firstInfo = MockAccessibilityService.createDefaultInfo();
@@ -356,7 +371,7 @@
secondService.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
@@ -366,12 +381,12 @@
@LargeTest
public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType()
throws Exception {
- // set the accessibility setting value
- ensureAccessibilityEnabled(mContext, true);
-
// enable the mock accessibility services
ensureOnlyMockServicesEnabled(mContext, true, true);
+ // set the accessibility setting value
+ ensureAccessibilityEnabled(mContext, true);
+
// configure the first mock service
MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
@@ -395,7 +410,7 @@
secondService.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
@@ -405,12 +420,12 @@
@LargeTest
public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_OneDefault()
throws Exception {
- // set the accessibility setting value
- ensureAccessibilityEnabled(mContext, true);
-
// enable the mock accessibility services
ensureOnlyMockServicesEnabled(mContext, true, true);
+ // set the accessibility setting value
+ ensureAccessibilityEnabled(mContext, true);
+
// configure the first mock service
MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo();
@@ -436,7 +451,7 @@
secondService.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
@@ -446,12 +461,12 @@
@LargeTest
public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_TwoDefault()
throws Exception {
- // set the accessibility setting value
- ensureAccessibilityEnabled(mContext, true);
-
// enable the mock accessibility services
ensureOnlyMockServicesEnabled(mContext, true, true);
+ // set the accessibility setting value
+ ensureAccessibilityEnabled(mContext, true);
+
// configure the first mock service
MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo();
@@ -479,7 +494,7 @@
secondService.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
@@ -488,12 +503,12 @@
@LargeTest
public void testInterrupt() throws Exception {
- // set the accessibility setting value
- ensureAccessibilityEnabled(mContext, true);
-
// enable the mock accessibility services
ensureOnlyMockServicesEnabled(mContext, true, true);
+ // set the accessibility setting value
+ ensureAccessibilityEnabled(mContext, true);
+
// configure the first mock service
MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
@@ -514,7 +529,7 @@
secondService.replay();
// call the method under test
- mManagerService.interrupt(UserHandle.USER_OWNER);
+ mManagerService.interrupt(UserHandle.USER_CURRENT);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
@@ -534,7 +549,7 @@
sentEvent.setContentDescription("ContentDescription");
sentEvent.setCurrentItemIndex(1);
sentEvent.setEnabled(true);
- sentEvent.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ sentEvent.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
sentEvent.setEventTime(1000);
sentEvent.setFromIndex(1);
sentEvent.setFullScreen(true);
@@ -568,8 +583,8 @@
* @throws Exception If any error occurs.
*/
private void ensureAccessibilityEnabled(Context context, boolean enabled) throws Exception {
- boolean isEnabled = (Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1 ? true : false);
+ boolean isEnabled = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
if (isEnabled == enabled) {
return;
@@ -608,13 +623,14 @@
servicesToEnable.append(MySecondMockAccessibilityService.sComponentName).append(":");
}
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, servicesToEnable.toString());
+
+ // Optimization. If things will not change, we don't have to do anything.
if (servicesToEnable.equals(enabledServices)) {
return;
}
- Settings.Secure.putString(context.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, servicesToEnable.toString());
-
// we have enabled the services of interest and need to wait until they
// are instantiated and started (if needed) and the system binds to them
boolean firstMockServiceOK = false;
@@ -664,13 +680,13 @@
throws Exception {
Exception lastVerifyException = null;
long beginTime = SystemClock.uptimeMillis();
- long pollTmeout = TIMEOUT_BINDER_CALL / 5;
+ long pollTimeout = TIMEOUT_BINDER_CALL / 5;
// poll until the timeout has elapsed
while (SystemClock.uptimeMillis() - beginTime < TIMEOUT_BINDER_CALL) {
// sleep first since immediate call will always fail
try {
- Thread.sleep(pollTmeout);
+ Thread.sleep(pollTimeout);
} catch (InterruptedException ie) {
/* ignore */
}
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
index e7366ea..026a2ad 100644
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
@@ -16,14 +16,11 @@
package com.android.server;
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.reportMatcher;
-import static org.easymock.EasyMock.reset;
-import static org.easymock.EasyMock.verify;
-
-import org.easymock.IArgumentMatcher;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.os.UserHandle;
@@ -35,6 +32,9 @@
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
import java.util.ArrayList;
import java.util.List;
@@ -49,78 +49,65 @@
*/
public static final long TIMEOUT_BINDER_CALL = 50;
- /**
- * The reusable mock {@link IAccessibilityManager}.
- */
- private final IAccessibilityManager mMockServiceInterface =
- createStrictMock(IAccessibilityManager.class);
+ @Mock
+ private IAccessibilityManager mMockService;
@Override
public void setUp() throws Exception {
- reset(mMockServiceInterface);
+ MockitoAnnotations.initMocks(this);
+ }
+
+ private AccessibilityManager createManager(boolean enabled) throws Exception {
+ if (enabled) {
+ when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
+ .thenReturn(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
+ } else {
+ when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
+ .thenReturn(0);
+ }
+
+ AccessibilityManager manager =
+ new AccessibilityManager(mContext, mMockService, UserHandle.USER_CURRENT);
+
+ verify(mMockService).addClient(any(IAccessibilityManagerClient.class), anyInt());
+
+ return manager;
}
@MediumTest
public void testGetAccessibilityServiceList() throws Exception {
// create a list of installed accessibility services the mock service returns
- List<AccessibilityServiceInfo> expectedServices = new ArrayList<AccessibilityServiceInfo>();
+ List<AccessibilityServiceInfo> expectedServices = new ArrayList<>();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
accessibilityServiceInfo.packageNames = new String[] { "foo.bar" };
expectedServices.add(accessibilityServiceInfo);
// configure the mock service behavior
- IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
- UserHandle.USER_OWNER)).andReturn(
- AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
- expect(mockServiceInterface.getInstalledAccessibilityServiceList(UserHandle.USER_OWNER))
- .andReturn(expectedServices);
- replay(mockServiceInterface);
+ when(mMockService.getInstalledAccessibilityServiceList(anyInt()))
+ .thenReturn(expectedServices);
// invoke the method under test
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
- UserHandle.USER_OWNER);
+ AccessibilityManager manager = createManager(true);
List<AccessibilityServiceInfo> receivedServices =
- manager.getInstalledAccessibilityServiceList();
+ manager.getInstalledAccessibilityServiceList();
+ verify(mMockService).getInstalledAccessibilityServiceList(UserHandle.USER_CURRENT);
// check expected result (list equals() compares it contents as well)
- assertEquals("All expected services must be returned", receivedServices, expectedServices);
-
- // verify the mock service was properly called
- verify(mockServiceInterface);
+ assertEquals("All expected services must be returned", expectedServices, receivedServices);
}
@MediumTest
public void testInterrupt() throws Exception {
- // configure the mock service behavior
- IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
- UserHandle.USER_OWNER)).andReturn(
- AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
- mockServiceInterface.interrupt(UserHandle.USER_OWNER);
- replay(mockServiceInterface);
-
- // invoke the method under test
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
- UserHandle.USER_OWNER);
+ AccessibilityManager manager = createManager(true);
manager.interrupt();
- // verify the mock service was properly called
- verify(mockServiceInterface);
+ verify(mMockService).interrupt(UserHandle.USER_CURRENT);
}
@LargeTest
public void testIsEnabled() throws Exception {
- // configure the mock service behavior
- IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
- UserHandle.USER_OWNER)).andReturn(
- AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
- replay(mockServiceInterface);
-
// invoke the method under test
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
- UserHandle.USER_OWNER);
+ AccessibilityManager manager = createManager(true);
boolean isEnabledServiceEnabled = manager.isEnabled();
// check expected result
@@ -138,63 +125,32 @@
// check expected result
assertFalse("Must be disabled since the mock service is disabled",
isEnabledServcieDisabled);
-
- // verify the mock service was properly called
- verify(mockServiceInterface);
}
@MediumTest
public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception {
- // create an event to be dispatched
AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
- // configure the mock service behavior
- IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
- UserHandle.USER_OWNER)).andReturn(
- AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
- expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent),
- UserHandle.USER_OWNER)).andReturn(true);
- expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent),
- UserHandle.USER_OWNER)).andReturn(false);
- replay(mockServiceInterface);
+ when(mMockService.sendAccessibilityEvent(eq(sentEvent), anyInt()))
+ .thenReturn(true /* should recycle event object */)
+ .thenReturn(false /* should not recycle event object */);
- // invoke the method under test (manager and service in different processes)
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
- UserHandle.USER_OWNER);
+ AccessibilityManager manager = createManager(true);
manager.sendAccessibilityEvent(sentEvent);
- // check expected result
- AccessibilityEvent nextEventDifferentProcesses = AccessibilityEvent.obtain();
- assertSame("The manager and the service are in different processes, so the event must be " +
- "recycled", sentEvent, nextEventDifferentProcesses);
+ assertSame("The event should be recycled.", sentEvent, AccessibilityEvent.obtain());
- // invoke the method under test (manager and service in the same process)
manager.sendAccessibilityEvent(sentEvent);
- // check expected result
- AccessibilityEvent nextEventSameProcess = AccessibilityEvent.obtain();
- assertNotSame("The manager and the service are in the same process, so the event must not" +
- "be recycled", sentEvent, nextEventSameProcess);
-
- // verify the mock service was properly called
- verify(mockServiceInterface);
+ assertNotSame("The event should not be recycled.", sentEvent, AccessibilityEvent.obtain());
}
@MediumTest
public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception {
- // create an event to be dispatched
AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
- // configure the mock service behavior
- IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
- UserHandle.USER_OWNER)).andReturn(0);
- replay(mockServiceInterface);
+ AccessibilityManager manager = createManager(false /* disabled */);
- // invoke the method under test (accessibility disabled)
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
- UserHandle.USER_OWNER);
try {
manager.sendAccessibilityEvent(sentEvent);
fail("No accessibility events are sent if accessibility is disabled");
@@ -202,73 +158,5 @@
// check expected result
assertEquals("Accessibility off. Did you forget to check that?", ise.getMessage());
}
-
- // verify the mock service was properly called
- verify(mockServiceInterface);
- }
-
- /**
- * Determines if an {@link AccessibilityEvent} passed as a method argument
- * matches expectations.
- *
- * @param matched The event to check.
- * @return True if expectations are matched.
- */
- private static AccessibilityEvent eqAccessibilityEvent(AccessibilityEvent matched) {
- reportMatcher(new AccessibilityEventMather(matched));
- return null;
- }
-
- /**
- * Determines if an {@link IAccessibilityManagerClient} passed as a method argument
- * matches expectations which in this case are that any instance is accepted.
- *
- * @return <code>null</code>.
- */
- private static IAccessibilityManagerClient anyIAccessibilityManagerClient() {
- reportMatcher(new AnyIAccessibilityManagerClientMather());
- return null;
- }
-
- /**
- * Matcher for {@link AccessibilityEvent}s.
- */
- private static class AccessibilityEventMather implements IArgumentMatcher {
- private AccessibilityEvent mExpectedEvent;
-
- public AccessibilityEventMather(AccessibilityEvent expectedEvent) {
- mExpectedEvent = expectedEvent;
- }
-
- public boolean matches(Object matched) {
- if (!(matched instanceof AccessibilityEvent)) {
- return false;
- }
- AccessibilityEvent receivedEvent = (AccessibilityEvent) matched;
- return mExpectedEvent.getEventType() == receivedEvent.getEventType();
- }
-
- public void appendTo(StringBuffer buffer) {
- buffer.append("sendAccessibilityEvent()");
- buffer.append(" with event type \"");
- buffer.append(mExpectedEvent.getEventType());
- buffer.append("\"");
- }
- }
-
- /**
- * Matcher for {@link IAccessibilityManagerClient}s.
- */
- private static class AnyIAccessibilityManagerClientMather implements IArgumentMatcher {
- public boolean matches(Object matched) {
- if (!(matched instanceof IAccessibilityManagerClient)) {
- return false;
- }
- return true;
- }
-
- public void appendTo(StringBuffer buffer) {
- buffer.append("addClient() with any IAccessibilityManagerClient");
- }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index b4c76b7..97e16da 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -20,35 +20,9 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.getNetworkTypeName;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_IA;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
+import static android.net.NetworkCapabilities.*;
+
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -58,8 +32,12 @@
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.ConnectivityManager.PacketKeepalive;
+import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgent;
@@ -74,8 +52,11 @@
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.MessageQueue.IdleHandler;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
@@ -84,10 +65,10 @@
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
-import org.mockito.ArgumentCaptor;
-
import java.net.InetAddress;
-import java.util.concurrent.Future;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -99,24 +80,7 @@
public class ConnectivityServiceTest extends AndroidTestCase {
private static final String TAG = "ConnectivityServiceTest";
- private static final String MOBILE_IFACE = "rmnet3";
- private static final String WIFI_IFACE = "wlan6";
-
- private static final RouteInfo MOBILE_ROUTE_V4 = RouteInfo.makeHostRoute(parse("10.0.0.33"),
- MOBILE_IFACE);
- private static final RouteInfo MOBILE_ROUTE_V6 = RouteInfo.makeHostRoute(parse("fd00::33"),
- MOBILE_IFACE);
-
- private static final RouteInfo WIFI_ROUTE_V4 = RouteInfo.makeHostRoute(parse("192.168.0.66"),
- parse("192.168.0.1"),
- WIFI_IFACE);
- private static final RouteInfo WIFI_ROUTE_V6 = RouteInfo.makeHostRoute(parse("fd00::66"),
- parse("fd00::"),
- WIFI_IFACE);
-
- private INetworkManagementService mNetManager;
- private INetworkStatsService mStatsService;
- private INetworkPolicyManager mPolicyService;
+ private static final int TIMEOUT_MS = 500;
private BroadcastInterceptingContext mServiceContext;
private WrappedConnectivityService mService;
@@ -148,14 +112,93 @@
}
}
+ /**
+ * A subclass of HandlerThread that allows callers to wait for it to become idle. waitForIdle
+ * will return immediately if the handler is already idle.
+ */
+ private class IdleableHandlerThread extends HandlerThread {
+ private IdleHandler mIdleHandler;
+
+ public IdleableHandlerThread(String name) {
+ super(name);
+ }
+
+ public void waitForIdle(int timeoutMs) {
+ final ConditionVariable cv = new ConditionVariable();
+ final MessageQueue queue = getLooper().getQueue();
+
+ synchronized (queue) {
+ if (queue.isIdle()) {
+ return;
+ }
+
+ assertNull("BUG: only one idle handler allowed", mIdleHandler);
+ mIdleHandler = new IdleHandler() {
+ public boolean queueIdle() {
+ cv.open();
+ mIdleHandler = null;
+ return false; // Remove the handler.
+ }
+ };
+ queue.addIdleHandler(mIdleHandler);
+ }
+
+ if (!cv.block(timeoutMs)) {
+ fail("HandlerThread " + getName() +
+ " did not become idle after " + timeoutMs + " ms");
+ queue.removeIdleHandler(mIdleHandler);
+ }
+ }
+ }
+
+ // Tests that IdleableHandlerThread works as expected.
+ public void testIdleableHandlerThread() {
+ final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng.
+
+ // Tests that waitForIdle returns immediately if the service is already idle.
+ for (int i = 0; i < attempts; i++) {
+ mService.waitForIdle();
+ }
+
+ // Bring up a network that we can use to send messages to ConnectivityService.
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ waitFor(cv);
+ Network n = mWiFiNetworkAgent.getNetwork();
+ assertNotNull(n);
+
+ // Tests that calling waitForIdle waits for messages to be processed.
+ for (int i = 0; i < attempts; i++) {
+ mWiFiNetworkAgent.setSignalStrength(i);
+ mService.waitForIdle();
+ assertEquals(i, mCm.getNetworkCapabilities(n).getSignalStrength());
+ }
+
+ // Ensure that not calling waitForIdle causes a race condition.
+ for (int i = 0; i < attempts; i++) {
+ mWiFiNetworkAgent.setSignalStrength(i);
+ if (i != mCm.getNetworkCapabilities(n).getSignalStrength()) {
+ // We hit a race condition, as expected. Pass the test.
+ return;
+ }
+ }
+
+ // No race? There is a bug in this test.
+ fail("expected race condition at least once in " + attempts + " attempts");
+ }
+
private class MockNetworkAgent {
private final WrappedNetworkMonitor mWrappedNetworkMonitor;
private final NetworkInfo mNetworkInfo;
private final NetworkCapabilities mNetworkCapabilities;
- private final Thread mThread;
+ private final IdleableHandlerThread mHandlerThread;
private final ConditionVariable mDisconnected = new ConditionVariable();
private int mScore;
private NetworkAgent mNetworkAgent;
+ private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
+ private int mStopKeepaliveError = PacketKeepalive.NO_KEEPALIVE;
+ private Integer mExpectedKeepaliveSlot = null;
MockNetworkAgent(int transport) {
final int type = transportToLegacyType(transport);
@@ -173,26 +216,42 @@
default:
throw new UnsupportedOperationException("unimplemented network type");
}
- final ConditionVariable initComplete = new ConditionVariable();
- final ConditionVariable networkMonitorAvailable = mService.getNetworkMonitorCreatedCV();
- mThread = new Thread() {
- public void run() {
- Looper.prepare();
- mNetworkAgent = new NetworkAgent(Looper.myLooper(), mServiceContext,
- "Mock" + typeName, mNetworkInfo, mNetworkCapabilities,
- new LinkProperties(), mScore, new NetworkMisc()) {
- public void unwanted() { mDisconnected.open(); }
- };
- initComplete.open();
- Looper.loop();
+ mHandlerThread = new IdleableHandlerThread("Mock-" + typeName);
+ mHandlerThread.start();
+ mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext,
+ "Mock-" + typeName, mNetworkInfo, mNetworkCapabilities,
+ new LinkProperties(), mScore, new NetworkMisc()) {
+ @Override
+ public void unwanted() { mDisconnected.open(); }
+
+ @Override
+ public void startPacketKeepalive(Message msg) {
+ int slot = msg.arg1;
+ if (mExpectedKeepaliveSlot != null) {
+ assertEquals((int) mExpectedKeepaliveSlot, slot);
+ }
+ onPacketKeepaliveEvent(slot, mStartKeepaliveError);
+ }
+
+ @Override
+ public void stopPacketKeepalive(Message msg) {
+ onPacketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
}
};
- mThread.start();
- waitFor(initComplete);
- waitFor(networkMonitorAvailable);
+ // Waits for the NetworkAgent to be registered, which includes the creation of the
+ // NetworkMonitor.
+ mService.waitForIdle();
mWrappedNetworkMonitor = mService.getLastCreatedWrappedNetworkMonitor();
}
+ public void waitForIdle(int timeoutMs) {
+ mHandlerThread.waitForIdle(timeoutMs);
+ }
+
+ public void waitForIdle() {
+ waitForIdle(TIMEOUT_MS);
+ }
+
public void adjustScore(int change) {
mScore += change;
mNetworkAgent.sendNetworkScore(mScore);
@@ -203,6 +262,11 @@
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
}
+ public void setSignalStrength(int signalStrength) {
+ mNetworkCapabilities.setSignalStrength(signalStrength);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
public void connectWithoutInternet() {
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -272,15 +336,46 @@
public WrappedNetworkMonitor getWrappedNetworkMonitor() {
return mWrappedNetworkMonitor;
}
+
+ public void sendLinkProperties(LinkProperties lp) {
+ mNetworkAgent.sendLinkProperties(lp);
+ }
+
+ public void setStartKeepaliveError(int error) {
+ mStartKeepaliveError = error;
+ }
+
+ public void setStopKeepaliveError(int error) {
+ mStopKeepaliveError = error;
+ }
+
+ public void setExpectedKeepaliveSlot(Integer slot) {
+ mExpectedKeepaliveSlot = slot;
+ }
}
+ /**
+ * A NetworkFactory that allows tests to wait until any in-flight NetworkRequest add or remove
+ * operations have been processed. Before ConnectivityService can add or remove any requests,
+ * the factory must be told to expect those operations by calling expectAddRequests or
+ * expectRemoveRequests.
+ */
private static class MockNetworkFactory extends NetworkFactory {
private final ConditionVariable mNetworkStartedCV = new ConditionVariable();
private final ConditionVariable mNetworkStoppedCV = new ConditionVariable();
- private final ConditionVariable mNetworkRequestedCV = new ConditionVariable();
- private final ConditionVariable mNetworkReleasedCV = new ConditionVariable();
private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false);
+ // Used to expect that requests be removed or added on a separate thread, without sleeping.
+ // Callers can call either expectAddRequests() or expectRemoveRequests() exactly once, then
+ // cause some other thread to add or remove requests, then call waitForRequests(). We can
+ // either expect requests to be added or removed, but not both, because CountDownLatch can
+ // only count in one direction.
+ private CountDownLatch mExpectations;
+
+ // Whether we are currently expecting requests to be added or removed. Valid only if
+ // mExpectations is non-null.
+ private boolean mExpectingAdditions;
+
public MockNetworkFactory(Looper looper, Context context, String logTag,
NetworkCapabilities filter) {
super(looper, context, logTag, filter);
@@ -314,28 +409,75 @@
return mNetworkStoppedCV;
}
- protected void needNetworkFor(NetworkRequest networkRequest, int score) {
- super.needNetworkFor(networkRequest, score);
- mNetworkRequestedCV.open();
+ @Override
+ protected void handleAddRequest(NetworkRequest request, int score) {
+ // If we're expecting anything, we must be expecting additions.
+ if (mExpectations != null && !mExpectingAdditions) {
+ fail("Can't add requests while expecting requests to be removed");
+ }
+
+ // Add the request.
+ super.handleAddRequest(request, score);
+
+ // Reduce the number of request additions we're waiting for.
+ if (mExpectingAdditions) {
+ assertTrue("Added more requests than expected", mExpectations.getCount() > 0);
+ mExpectations.countDown();
+ }
}
- protected void releaseNetworkFor(NetworkRequest networkRequest) {
- super.releaseNetworkFor(networkRequest);
- mNetworkReleasedCV.open();
+ @Override
+ protected void handleRemoveRequest(NetworkRequest request) {
+ // If we're expecting anything, we must be expecting removals.
+ if (mExpectations != null && mExpectingAdditions) {
+ fail("Can't remove requests while expecting requests to be added");
+ }
+
+ // Remove the request.
+ super.handleRemoveRequest(request);
+
+ // Reduce the number of request removals we're waiting for.
+ if (!mExpectingAdditions) {
+ assertTrue("Removed more requests than expected", mExpectations.getCount() > 0);
+ mExpectations.countDown();
+ }
}
- public ConditionVariable getNetworkRequestedCV() {
- mNetworkRequestedCV.close();
- return mNetworkRequestedCV;
+ private void assertNoExpectations() {
+ if (mExpectations != null) {
+ fail("Can't add expectation, " + mExpectations.getCount() + " already pending");
+ }
}
- public ConditionVariable getNetworkReleasedCV() {
- mNetworkReleasedCV.close();
- return mNetworkReleasedCV;
+ // Expects that count requests will be added.
+ public void expectAddRequests(final int count) {
+ assertNoExpectations();
+ mExpectingAdditions = true;
+ mExpectations = new CountDownLatch(count);
}
- public void waitForNetworkRequests(final int count) {
- waitFor(new Criteria() { public boolean get() { return count == getRequestCount(); } });
+ // Expects that count requests will be removed.
+ public void expectRemoveRequests(final int count) {
+ assertNoExpectations();
+ mExpectingAdditions = false;
+ mExpectations = new CountDownLatch(count);
+ }
+
+ // Waits for the expected request additions or removals to happen within a timeout.
+ public void waitForRequests() throws InterruptedException {
+ assertNotNull("Nothing to wait for", mExpectations);
+ mExpectations.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ final long count = mExpectations.getCount();
+ final String msg = count + " requests still not " +
+ (mExpectingAdditions ? "added" : "removed") +
+ " after " + TIMEOUT_MS + " ms";
+ assertEquals(msg, 0, count);
+ mExpectations = null;
+ }
+
+ public void waitForNetworkRequests(final int count) throws InterruptedException {
+ waitForRequests();
+ assertEquals(count, getMyRequestCount());
}
}
@@ -356,7 +498,6 @@
}
private class WrappedConnectivityService extends ConnectivityService {
- private final ConditionVariable mNetworkMonitorCreated = new ConditionVariable();
private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
@@ -365,6 +506,11 @@
}
@Override
+ protected HandlerThread createHandlerThread() {
+ return new IdleableHandlerThread("WrappedConnectivityService");
+ }
+
+ @Override
protected int getDefaultTcpRwnd() {
// Prevent wrapped ConnectivityService from trying to write to SystemProperties.
return 0;
@@ -397,7 +543,6 @@
final WrappedNetworkMonitor monitor = new WrappedNetworkMonitor(context, handler, nai,
defaultRequest);
mLastCreatedNetworkMonitor = monitor;
- mNetworkMonitorCreated.open();
return monitor;
}
@@ -405,10 +550,14 @@
return mLastCreatedNetworkMonitor;
}
- public ConditionVariable getNetworkMonitorCreatedCV() {
- mNetworkMonitorCreated.close();
- return mNetworkMonitorCreated;
+ public void waitForIdle(int timeoutMs) {
+ ((IdleableHandlerThread) mHandlerThread).waitForIdle(timeoutMs);
}
+
+ public void waitForIdle() {
+ waitForIdle(TIMEOUT_MS);
+ }
+
}
private interface Criteria {
@@ -431,23 +580,11 @@
}
/**
- * Wait up to 500ms for {@code conditonVariable} to open.
- * Fails if 500ms goes by before {@code conditionVariable} opens.
+ * Wait up to TIMEOUT_MS for {@code conditionVariable} to open.
+ * Fails if TIMEOUT_MS goes by before {@code conditionVariable} opens.
*/
static private void waitFor(ConditionVariable conditionVariable) {
- assertTrue(conditionVariable.block(500));
- }
-
- /**
- * This should only be used to verify that nothing happens, in other words that no unexpected
- * changes occur. It should never be used to wait for a specific positive signal to occur.
- */
- private void shortSleep() {
- // TODO: Instead of sleeping, instead wait for all message loops to idle.
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- }
+ assertTrue(conditionVariable.block(TIMEOUT_MS));
}
@Override
@@ -455,13 +592,11 @@
super.setUp();
mServiceContext = new MockContext(getContext());
+ mService = new WrappedConnectivityService(mServiceContext,
+ mock(INetworkManagementService.class),
+ mock(INetworkStatsService.class),
+ mock(INetworkPolicyManager.class));
- mNetManager = mock(INetworkManagementService.class);
- mStatsService = mock(INetworkStatsService.class);
- mPolicyService = mock(INetworkPolicyManager.class);
-
- mService = new WrappedConnectivityService(
- mServiceContext, mNetManager, mStatsService, mPolicyService);
mService.systemReady();
mCm = new ConnectivityManager(getContext(), mService);
}
@@ -583,11 +718,11 @@
// Test bringing up unvalidated cellular
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
- shortSleep();
+ mService.waitForIdle();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test cellular disconnect.
mCellNetworkAgent.disconnect();
- shortSleep();
+ mService.waitForIdle();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up validated cellular
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -797,6 +932,11 @@
LOST
}
+ /**
+ * Utility NetworkCallback for testing. The caller must explicitly test for all the callbacks
+ * this class receives, by calling expectCallback() exactly once each time a callback is
+ * received. assertNoCallback may be called at any time.
+ */
private class TestNetworkCallback extends NetworkCallback {
private final ConditionVariable mConditionVariable = new ConditionVariable();
private CallbackState mLastCallback = CallbackState.NONE;
@@ -819,14 +959,15 @@
mConditionVariable.open();
}
- ConditionVariable getConditionVariable() {
+ void expectCallback(CallbackState state) {
+ waitFor(mConditionVariable);
+ assertEquals(state, mLastCallback);
mLastCallback = CallbackState.NONE;
mConditionVariable.close();
- return mConditionVariable;
}
- CallbackState getLastCallback() {
- return mLastCallback;
+ void assertNoCallback() {
+ assertEquals(CallbackState.NONE, mLastCallback);
}
}
@@ -842,98 +983,68 @@
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
// Test unvalidated networks
- ConditionVariable cellCv = cellNetworkCallback.getConditionVariable();
- ConditionVariable wifiCv = wifiNetworkCallback.getConditionVariable();
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
- waitFor(cellCv);
- assertEquals(CallbackState.AVAILABLE, cellNetworkCallback.getLastCallback());
- assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback());
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE);
+ wifiNetworkCallback.assertNoCallback();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
- cellCv = cellNetworkCallback.getConditionVariable();
- wifiCv = wifiNetworkCallback.getConditionVariable();
// This should not trigger spurious onAvailable() callbacks, b/21762680.
mCellNetworkAgent.adjustScore(-1);
- shortSleep();
- assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback());
- assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback());
+ mService.waitForIdle();
+ wifiNetworkCallback.assertNoCallback();
+ cellNetworkCallback.assertNoCallback();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- cellCv = cellNetworkCallback.getConditionVariable();
- wifiCv = wifiNetworkCallback.getConditionVariable();
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- waitFor(wifiCv);
- assertEquals(CallbackState.AVAILABLE, wifiNetworkCallback.getLastCallback());
- assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback());
+ wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE);
+ cellNetworkCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
- cellCv = cellNetworkCallback.getConditionVariable();
- wifiCv = wifiNetworkCallback.getConditionVariable();
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent.disconnect();
- waitFor(wifiCv);
- assertEquals(CallbackState.LOST, wifiNetworkCallback.getLastCallback());
- assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback());
+ wifiNetworkCallback.expectCallback(CallbackState.LOST);
+ cellNetworkCallback.assertNoCallback();
waitFor(cv);
- cellCv = cellNetworkCallback.getConditionVariable();
- wifiCv = wifiNetworkCallback.getConditionVariable();
cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent.disconnect();
- waitFor(cellCv);
- assertEquals(CallbackState.LOST, cellNetworkCallback.getLastCallback());
- assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback());
+ cellNetworkCallback.expectCallback(CallbackState.LOST);
+ wifiNetworkCallback.assertNoCallback();
waitFor(cv);
// Test validated networks
-
- cellCv = cellNetworkCallback.getConditionVariable();
- wifiCv = wifiNetworkCallback.getConditionVariable();
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- waitFor(cellCv);
- assertEquals(CallbackState.AVAILABLE, cellNetworkCallback.getLastCallback());
- assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback());
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE);
+ wifiNetworkCallback.assertNoCallback();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- cellCv = cellNetworkCallback.getConditionVariable();
- wifiCv = wifiNetworkCallback.getConditionVariable();
// This should not trigger spurious onAvailable() callbacks, b/21762680.
mCellNetworkAgent.adjustScore(-1);
- shortSleep();
- assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback());
- assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback());
+ mService.waitForIdle();
+ wifiNetworkCallback.assertNoCallback();
+ cellNetworkCallback.assertNoCallback();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- cellCv = cellNetworkCallback.getConditionVariable();
- wifiCv = wifiNetworkCallback.getConditionVariable();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- waitFor(wifiCv);
- assertEquals(CallbackState.AVAILABLE, wifiNetworkCallback.getLastCallback());
- waitFor(cellCv);
- assertEquals(CallbackState.LOSING, cellNetworkCallback.getLastCallback());
+ wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE);
+ cellNetworkCallback.expectCallback(CallbackState.LOSING);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- cellCv = cellNetworkCallback.getConditionVariable();
- wifiCv = wifiNetworkCallback.getConditionVariable();
mWiFiNetworkAgent.disconnect();
- waitFor(wifiCv);
- assertEquals(CallbackState.LOST, wifiNetworkCallback.getLastCallback());
- assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback());
+ wifiNetworkCallback.expectCallback(CallbackState.LOST);
+ cellNetworkCallback.assertNoCallback();
- cellCv = cellNetworkCallback.getConditionVariable();
- wifiCv = wifiNetworkCallback.getConditionVariable();
mCellNetworkAgent.disconnect();
- waitFor(cellCv);
- assertEquals(CallbackState.LOST, cellNetworkCallback.getLastCallback());
- assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback());
+ cellNetworkCallback.expectCallback(CallbackState.LOST);
+ wifiNetworkCallback.assertNoCallback();
}
private void tryNetworkFactoryRequests(int capability) throws Exception {
@@ -957,18 +1068,21 @@
mServiceContext, "testFactory", filter);
testFactory.setScoreFilter(40);
ConditionVariable cv = testFactory.getNetworkStartedCV();
+ testFactory.expectAddRequests(1);
testFactory.register();
+ testFactory.waitForNetworkRequests(1);
int expectedRequestCount = 1;
NetworkCallback networkCallback = null;
// For non-INTERNET capabilities we cannot rely on the default request being present, so
// add one.
if (capability != NET_CAPABILITY_INTERNET) {
- testFactory.waitForNetworkRequests(1);
assertFalse(testFactory.getMyStartRequested());
NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build();
networkCallback = new NetworkCallback();
+ testFactory.expectAddRequests(1);
mCm.requestNetwork(request, networkCallback);
expectedRequestCount++;
+ testFactory.waitForNetworkRequests(expectedRequestCount);
}
waitFor(cv);
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
@@ -981,13 +1095,20 @@
// unvalidated penalty.
testAgent.adjustScore(40);
cv = testFactory.getNetworkStoppedCV();
+
+ // When testAgent connects, ConnectivityService will re-send us all current requests with
+ // the new score. There are expectedRequestCount such requests, and we must wait for all of
+ // them.
+ testFactory.expectAddRequests(expectedRequestCount);
testAgent.connect(false);
testAgent.addCapability(capability);
waitFor(cv);
- assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
+ testFactory.waitForNetworkRequests(expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Bring in a bunch of requests.
+ testFactory.expectAddRequests(10);
+ assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
ConnectivityManager.NetworkCallback[] networkCallbacks =
new ConnectivityManager.NetworkCallback[10];
for (int i = 0; i< networkCallbacks.length; i++) {
@@ -1000,6 +1121,7 @@
assertFalse(testFactory.getMyStartRequested());
// Remove the requests.
+ testFactory.expectRemoveRequests(10);
for (int i = 0; i < networkCallbacks.length; i++) {
mCm.unregisterNetworkCallback(networkCallbacks[i]);
}
@@ -1088,10 +1210,8 @@
// Test bringing up unvalidated cellular with MMS
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
- cv = networkCallback.getConditionVariable();
mCellNetworkAgent.connectWithoutInternet();
- waitFor(cv);
- assertEquals(CallbackState.AVAILABLE, networkCallback.getLastCallback());
+ networkCallback.expectCallback(CallbackState.AVAILABLE);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test releasing NetworkRequest disconnects cellular with MMS
cv = mCellNetworkAgent.getDisconnectedCV();
@@ -1114,12 +1234,10 @@
final TestNetworkCallback networkCallback = new TestNetworkCallback();
mCm.requestNetwork(builder.build(), networkCallback);
// Test bringing up MMS cellular network
- cv = networkCallback.getConditionVariable();
MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mmsNetworkAgent.connectWithoutInternet();
- waitFor(cv);
- assertEquals(CallbackState.AVAILABLE, networkCallback.getLastCallback());
+ networkCallback.expectCallback(CallbackState.AVAILABLE);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
cv = mmsNetworkAgent.getDisconnectedCV();
@@ -1139,133 +1257,245 @@
final NetworkRequest validatedRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_VALIDATED).build();
mCm.registerNetworkCallback(validatedRequest, validatedCallback);
- ConditionVariable validatedCv = validatedCallback.getConditionVariable();
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
- ConditionVariable cv = captivePortalCallback.getConditionVariable();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithCaptivePortal();
- waitFor(cv);
- assertEquals(CallbackState.AVAILABLE, captivePortalCallback.getLastCallback());
+ captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
// Take down network.
// Expect onLost callback.
- cv = captivePortalCallback.getConditionVariable();
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
- assertEquals(CallbackState.LOST, captivePortalCallback.getLastCallback());
+ captivePortalCallback.expectCallback(CallbackState.LOST);
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
- cv = captivePortalCallback.getConditionVariable();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithCaptivePortal();
- waitFor(cv);
- assertEquals(CallbackState.AVAILABLE, captivePortalCallback.getLastCallback());
+ captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
// Make captive portal disappear then revalidate.
// Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
- cv = captivePortalCallback.getConditionVariable();
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
- waitFor(cv);
- assertEquals(CallbackState.LOST, captivePortalCallback.getLastCallback());
+ captivePortalCallback.expectCallback(CallbackState.LOST);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- waitFor(validatedCv);
- assertEquals(CallbackState.AVAILABLE, validatedCallback.getLastCallback());
+ validatedCallback.expectCallback(CallbackState.AVAILABLE);
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
- validatedCv = validatedCallback.getConditionVariable();
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500;
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
- waitFor(validatedCv);
- assertEquals(CallbackState.LOST, validatedCallback.getLastCallback());
+ validatedCallback.expectCallback(CallbackState.LOST);
}
-// @Override
-// public void tearDown() throws Exception {
-// super.tearDown();
-// }
-//
-// public void testMobileConnectedAddedRoutes() throws Exception {
-// Future<?> nextConnBroadcast;
-//
-// // bring up mobile network
-// mMobile.info.setDetailedState(DetailedState.CONNECTED, null, null);
-// mMobile.link.setInterfaceName(MOBILE_IFACE);
-// mMobile.link.addRoute(MOBILE_ROUTE_V4);
-// mMobile.link.addRoute(MOBILE_ROUTE_V6);
-// mMobile.doReturnDefaults();
-//
-// cv = waitForConnectivityBroadcasts(1);
-// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-// waitFor(cv);
-//
-// // verify that both routes were added
-// int mobileNetId = mMobile.tracker.getNetwork().netId;
-// verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4));
-// verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6));
-// }
-//
-// public void testMobileWifiHandoff() throws Exception {
-// Future<?> nextConnBroadcast;
-//
-// // bring up mobile network
-// mMobile.info.setDetailedState(DetailedState.CONNECTED, null, null);
-// mMobile.link.setInterfaceName(MOBILE_IFACE);
-// mMobile.link.addRoute(MOBILE_ROUTE_V4);
-// mMobile.link.addRoute(MOBILE_ROUTE_V6);
-// mMobile.doReturnDefaults();
-//
-// cv = waitForConnectivityBroadcasts(1);
-// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-// waitFor(cv);
-//
-// reset(mNetManager);
-//
-// // now bring up wifi network
-// mWifi.info.setDetailedState(DetailedState.CONNECTED, null, null);
-// mWifi.link.setInterfaceName(WIFI_IFACE);
-// mWifi.link.addRoute(WIFI_ROUTE_V4);
-// mWifi.link.addRoute(WIFI_ROUTE_V6);
-// mWifi.doReturnDefaults();
-//
-// // expect that mobile will be torn down
-// doReturn(true).when(mMobile.tracker).teardown();
-//
-// cv = waitForConnectivityBroadcasts(1);
-// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mWifi.info).sendToTarget();
-// waitFor(cv);
-//
-// // verify that wifi routes added, and teardown requested
-// int wifiNetId = mWifi.tracker.getNetwork().netId;
-// verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V4));
-// verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V6));
-// verify(mMobile.tracker).teardown();
-//
-// int mobileNetId = mMobile.tracker.getNetwork().netId;
-//
-// reset(mNetManager, mMobile.tracker);
-//
-// // tear down mobile network, as requested
-// mMobile.info.setDetailedState(DetailedState.DISCONNECTED, null, null);
-// mMobile.link.clear();
-// mMobile.doReturnDefaults();
-//
-// cv = waitForConnectivityBroadcasts(1);
-// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-// waitFor(cv);
-//
-// verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4));
-// verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6));
-//
-// }
+ private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
- private static InetAddress parse(String addr) {
- return InetAddress.parseNumericAddress(addr);
+ public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
+
+ private class CallbackValue {
+ public CallbackType callbackType;
+ public int error;
+
+ public CallbackValue(CallbackType type) {
+ this.callbackType = type;
+ this.error = PacketKeepalive.SUCCESS;
+ assertTrue("onError callback must have error", type != CallbackType.ON_ERROR);
+ }
+
+ public CallbackValue(CallbackType type, int error) {
+ this.callbackType = type;
+ this.error = error;
+ assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof CallbackValue &&
+ this.callbackType == ((CallbackValue) o).callbackType &&
+ this.error == ((CallbackValue) o).error;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, error);
+ }
+ }
+
+ private LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>();
+
+ @Override
+ public void onStarted() {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED));
+ }
+
+ @Override
+ public void onStopped() {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED));
+ }
+
+ @Override
+ public void onError(int error) {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
+ }
+
+ private void expectCallback(CallbackValue callbackValue) {
+ try {
+ assertEquals(
+ callbackValue,
+ mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms");
+ }
+ }
+
+ public void expectStarted() {
+ expectCallback(new CallbackValue(CallbackType.ON_STARTED));
+ }
+
+ public void expectStopped() {
+ expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
+ }
+
+ public void expectError(int error) {
+ expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
+ }
}
+ private Network connectKeepaliveNetwork(LinkProperties lp) {
+ // Ensure the network is disconnected before we do anything.
+ if (mWiFiNetworkAgent != null) {
+ assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()));
+ }
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent.connect(true);
+ waitFor(cv);
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ mService.waitForIdle();
+ return mWiFiNetworkAgent.getNetwork();
+ }
+
+ public void testPacketKeepalives() throws Exception {
+ InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
+ InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
+ InetAddress myIPv6 = InetAddress.getByName("2001:db8::1");
+ InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8");
+ InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888");
+
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("wlan12");
+ lp.addLinkAddress(new LinkAddress(myIPv6, 64));
+ lp.addLinkAddress(new LinkAddress(myIPv4, 25));
+ lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234")));
+ lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
+
+ Network notMyNet = new Network(61234);
+ Network myNet = connectKeepaliveNetwork(lp);
+
+ TestKeepaliveCallback callback = new TestKeepaliveCallback();
+ PacketKeepalive ka;
+
+ // Attempt to start keepalives with invalid parameters and check for errors.
+ ka = mCm.startNattKeepalive(notMyNet, 25, callback, myIPv4, 1234, dstIPv4);
+ callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK);
+
+ ka = mCm.startNattKeepalive(myNet, 19, callback, notMyIPv4, 1234, dstIPv4);
+ callback.expectError(PacketKeepalive.ERROR_INVALID_INTERVAL);
+
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 1234, dstIPv6);
+ callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
+
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv6, 1234, dstIPv4);
+ callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
+
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv6, 1234, dstIPv6);
+ callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); // NAT-T is IPv4-only.
+
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 123456, dstIPv4);
+ callback.expectError(PacketKeepalive.ERROR_INVALID_PORT);
+
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 123456, dstIPv4);
+ callback.expectError(PacketKeepalive.ERROR_INVALID_PORT);
+
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+
+ // Check that a started keepalive can be stopped.
+ mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ callback.expectStarted();
+ mWiFiNetworkAgent.setStopKeepaliveError(PacketKeepalive.SUCCESS);
+ ka.stop();
+ callback.expectStopped();
+
+ // Check that deleting the IP address stops the keepalive.
+ LinkProperties bogusLp = new LinkProperties(lp);
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ callback.expectStarted();
+ bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25));
+ bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25));
+ mWiFiNetworkAgent.sendLinkProperties(bogusLp);
+ callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+
+ // Check that a started keepalive is stopped correctly when the network disconnects.
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ callback.expectStarted();
+ mWiFiNetworkAgent.disconnect();
+ callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK);
+
+ // ... and that stopping it after that has no adverse effects.
+ assertNull(mCm.getNetworkCapabilities(myNet));
+ ka.stop();
+
+ // Reconnect.
+ myNet = connectKeepaliveNetwork(lp);
+ mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
+
+ // Check things work as expected when the keepalive is stopped and the network disconnects.
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ callback.expectStarted();
+ ka.stop();
+ mWiFiNetworkAgent.disconnect();
+ mService.waitForIdle();
+ callback.expectStopped();
+
+ // Reconnect.
+ myNet = connectKeepaliveNetwork(lp);
+ mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
+
+ // Check that keepalive slots start from 1 and increment. The first one gets slot 1.
+ mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
+ ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ callback.expectStarted();
+
+ // The second one gets slot 2.
+ mWiFiNetworkAgent.setExpectedKeepaliveSlot(2);
+ TestKeepaliveCallback callback2 = new TestKeepaliveCallback();
+ PacketKeepalive ka2 = mCm.startNattKeepalive(myNet, 25, callback2, myIPv4, 6789, dstIPv4);
+ callback2.expectStarted();
+
+ // Now stop the first one and create a third. This also gets slot 1.
+ ka.stop();
+ callback.expectStopped();
+
+ mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
+ TestKeepaliveCallback callback3 = new TestKeepaliveCallback();
+ PacketKeepalive ka3 = mCm.startNattKeepalive(myNet, 25, callback3, myIPv4, 9876, dstIPv4);
+ callback3.expectStarted();
+
+ ka2.stop();
+ callback2.expectStopped();
+
+ ka3.stop();
+ callback3.expectStopped();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java b/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java
index 1bc9b86..e1c5cee 100644
--- a/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java
+++ b/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java
@@ -62,7 +62,7 @@
*/
public static AccessibilityServiceInfo createDefaultInfo() {
AccessibilityServiceInfo defaultInfo = new AccessibilityServiceInfo();
- defaultInfo.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED;
+ defaultInfo.eventTypes = AccessibilityEvent.TYPE_ANNOUNCEMENT;
defaultInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
defaultInfo.flags = 0;
defaultInfo.notificationTimeout = 0;
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 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/DeviceOwnerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DeviceOwnerTest.java
deleted file mode 100644
index 8ad7eea..0000000
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DeviceOwnerTest.java
+++ /dev/null
@@ -1,67 +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.content.ComponentName;
-import android.content.Context;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-
-/**
- * Tests for the DeviceOwner object that saves & loads device and policy owner information.
- * run this test with:
- * make -j FrameworksServicesTests
- * runtest --path frameworks/base/services/tests/servicestests/ \
- * src/com/android/server/devicepolicy/DeviceOwnerTest.java
- */
-public class DeviceOwnerTest extends AndroidTestCase {
-
- private static class DeviceOwnerSub extends DeviceOwner{
- private final File mLegacyFile;
- private final File mDeviceOwnerFile;
- private final File mProfileOwnerBase;
-
- public DeviceOwnerSub(Context context, File legacyFile, File deviceOwnerFile,
- File profileOwnerBase) {
- super(context);
- mLegacyFile = legacyFile;
- mDeviceOwnerFile = deviceOwnerFile;
- mProfileOwnerBase = profileOwnerBase;
- }
-
- @Override
- File getLegacyConfigFileWithTestOverride() {
- return mLegacyFile;
- }
-
- @Override
- File getDeviceOwnerFileWithTestOverride() {
- return mDeviceOwnerFile;
- }
-
- @Override
- File getProfileOwnerFileWithTestOverride(int userId) {
- return new File(mDeviceOwnerFile.getAbsoluteFile() + "-" + userId);
- }
- }
-
- // TODO Write tests
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
new file mode 100644
index 0000000..c2b8981
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -0,0 +1,46 @@
+/*
+ * 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.content.Context;
+import android.content.ContextWrapper;
+import android.os.UserManager;
+
+import static org.mockito.Mockito.mock;
+
+public class DpmMockContext extends ContextWrapper {
+ private final UserManager mMockUserManager;
+
+
+ public DpmMockContext(Context context) {
+ super(context);
+ mMockUserManager = mock(UserManager.class);
+ }
+
+ public UserManager getMockUserManager() {
+ return mMockUserManager;
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case Context.USER_SERVICE:
+ return mMockUserManager;
+ }
+ return super.getSystemService(name);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
new file mode 100644
index 0000000..445260b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.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.content.Context;
+import android.test.AndroidTestCase;
+
+public class DpmTestBase extends AndroidTestCase {
+ private DpmMockContext mMockContext;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mMockContext = new DpmMockContext(super.getContext());
+ }
+
+ @Override
+ public DpmMockContext getContext() {
+ return mMockContext;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
new file mode 100644
index 0000000..3b88fb1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -0,0 +1,481 @@
+/*
+ * 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.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;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+import junit.framework.Assert;
+
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for the DeviceOwner object that saves & loads device and policy owner information.
+ * run this test with:
+ m FrameworksServicesTests &&
+ adb install \
+ -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.devicepolicy.OwnersTest \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+
+ (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))))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ sb.append(System.lineSeparator());
+ }
+ }
+ return sb.toString();
+ }
+
+ private void createLegacyFile(File path, String content)
+ throws IOException {
+ path.getParentFile().mkdirs();
+
+ try (FileWriter writer = new FileWriter(path)) {
+ Log.i(TAG, "Writing to " + path);
+ Log.i(TAG, content);
+ writer.write(content);
+ }
+ }
+
+ private void addUsersToUserManager(int... userIds) {
+ final ArrayList<UserInfo> userInfos = new ArrayList<>();
+ for (int userId : userIds) {
+ final UserInfo ui = new UserInfo();
+ ui.id = userId;
+ userInfos.add(ui);
+ }
+ when(getContext().getMockUserManager().getUsers()).thenReturn(userInfos);
+ }
+
+ public void testUpgrade01() throws Exception {
+ addUsersToUserManager(10, 11, 20, 21);
+
+ // First, migrate.
+ {
+ final OwnersSub owners = new OwnersSub();
+
+ createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test01/input.xml"));
+
+ owners.load();
+
+ // The legacy file should be removed.
+ assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+
+ // File was empty, so no new files should be created.
+ assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
+
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(21).exists());
+
+ assertFalse(owners.hasDeviceOwner());
+ assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
+ assertFalse(owners.hasDeviceInitializer());
+ assertNull(owners.getSystemUpdatePolicy());
+ assertEquals(0, owners.getProfileOwnerKeys().size());
+ }
+
+ // Then re-read and check.
+ {
+ final OwnersSub owners = new OwnersSub();
+ owners.load();
+
+ assertFalse(owners.hasDeviceOwner());
+ assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
+ assertFalse(owners.hasDeviceInitializer());
+ assertNull(owners.getSystemUpdatePolicy());
+ assertEquals(0, owners.getProfileOwnerKeys().size());
+ }
+ }
+
+ public void testUpgrade02() throws Exception {
+ addUsersToUserManager(10, 11, 20, 21);
+
+ // First, migrate.
+ {
+ final OwnersSub owners = new OwnersSub();
+
+ createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test02/input.xml"));
+
+ owners.load();
+
+ // The legacy file should be removed.
+ assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+
+ assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists()); // TODO Check content
+
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(21).exists());
+
+ assertTrue(owners.hasDeviceOwner());
+ assertEquals(null, owners.getDeviceOwnerName());
+ assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
+ assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
+
+ assertFalse(owners.hasDeviceInitializer());
+ assertNull(owners.getSystemUpdatePolicy());
+ assertEquals(0, owners.getProfileOwnerKeys().size());
+ }
+
+ // Then re-read and check.
+ {
+ final OwnersSub owners = new OwnersSub();
+ owners.load();
+
+ assertTrue(owners.hasDeviceOwner());
+ assertEquals(null, owners.getDeviceOwnerName());
+ assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
+ assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
+
+ assertFalse(owners.hasDeviceInitializer());
+ assertNull(owners.getSystemUpdatePolicy());
+ assertEquals(0, owners.getProfileOwnerKeys().size());
+ }
+ }
+
+ public void testUpgrade03() throws Exception {
+ addUsersToUserManager(10, 11, 20, 21);
+
+ // First, migrate.
+ {
+ final OwnersSub owners = new OwnersSub();
+
+ createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test03/input.xml"));
+
+ owners.load();
+
+ // The legacy file should be removed.
+ assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+
+ assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
+
+ assertTrue(owners.getProfileOwnerFileWithTestOverride(10).exists());
+ assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(21).exists());
+
+ assertFalse(owners.hasDeviceOwner());
+ assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
+ assertFalse(owners.hasDeviceInitializer());
+ assertNull(owners.getSystemUpdatePolicy());
+
+ assertEquals(2, owners.getProfileOwnerKeys().size());
+ assertEquals(new ComponentName("com.google.android.testdpc",
+ "com.google.android.testdpc.DeviceAdminReceiver0"),
+ owners.getProfileOwnerComponent(10));
+ assertEquals("0", owners.getProfileOwnerName(10));
+ assertEquals("com.google.android.testdpc", owners.getProfileOwnerPackage(10));
+
+ assertEquals(new ComponentName("com.google.android.testdpc1", ""),
+ owners.getProfileOwnerComponent(11));
+ assertEquals("1", owners.getProfileOwnerName(11));
+ assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11));
+ }
+
+ // Then re-read and check.
+ {
+ final OwnersSub owners = new OwnersSub();
+ owners.load();
+
+ assertFalse(owners.hasDeviceOwner());
+ assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
+ assertFalse(owners.hasDeviceInitializer());
+ assertNull(owners.getSystemUpdatePolicy());
+
+ assertEquals(2, owners.getProfileOwnerKeys().size());
+ assertEquals(new ComponentName("com.google.android.testdpc",
+ "com.google.android.testdpc.DeviceAdminReceiver0"),
+ owners.getProfileOwnerComponent(10));
+ assertEquals("0", owners.getProfileOwnerName(10));
+ assertEquals("com.google.android.testdpc", owners.getProfileOwnerPackage(10));
+
+ assertEquals(new ComponentName("com.google.android.testdpc1", ""),
+ owners.getProfileOwnerComponent(11));
+ assertEquals("1", owners.getProfileOwnerName(11));
+ assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11));
+ }
+ }
+
+ public void testUpgrade04() throws Exception {
+ addUsersToUserManager(10, 11, 20, 21);
+
+ // First, migrate.
+ {
+ final OwnersSub owners = new OwnersSub();
+
+ createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test04/input.xml"));
+
+ owners.load();
+
+ // The legacy file should be removed.
+ assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+
+ assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
+
+ assertTrue(owners.getProfileOwnerFileWithTestOverride(10).exists());
+ assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(21).exists());
+
+ assertTrue(owners.hasDeviceOwner());
+ assertEquals(null, owners.getDeviceOwnerName());
+ assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
+ assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
+
+ assertTrue(owners.hasDeviceInitializer());
+ assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
+ assertNotNull(owners.getSystemUpdatePolicy());
+ assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
+
+ assertEquals(2, owners.getProfileOwnerKeys().size());
+ assertEquals(new ComponentName("com.google.android.testdpc",
+ "com.google.android.testdpc.DeviceAdminReceiver0"),
+ owners.getProfileOwnerComponent(10));
+ assertEquals("0", owners.getProfileOwnerName(10));
+ assertEquals("com.google.android.testdpc", owners.getProfileOwnerPackage(10));
+
+ assertEquals(new ComponentName("com.google.android.testdpc1", ""),
+ owners.getProfileOwnerComponent(11));
+ assertEquals("1", owners.getProfileOwnerName(11));
+ assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11));
+ }
+
+ // Then re-read and check.
+ {
+ final OwnersSub owners = new OwnersSub();
+ owners.load();
+
+ assertTrue(owners.hasDeviceOwner());
+ assertEquals(null, owners.getDeviceOwnerName());
+ assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
+ assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
+
+ assertTrue(owners.hasDeviceInitializer());
+ assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
+ assertNotNull(owners.getSystemUpdatePolicy());
+ assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
+
+ assertEquals(2, owners.getProfileOwnerKeys().size());
+ assertEquals(new ComponentName("com.google.android.testdpc",
+ "com.google.android.testdpc.DeviceAdminReceiver0"),
+ owners.getProfileOwnerComponent(10));
+ assertEquals("0", owners.getProfileOwnerName(10));
+ assertEquals("com.google.android.testdpc", owners.getProfileOwnerPackage(10));
+
+ assertEquals(new ComponentName("com.google.android.testdpc1", ""),
+ owners.getProfileOwnerComponent(11));
+ assertEquals("1", owners.getProfileOwnerName(11));
+ assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11));
+ }
+ }
+
+ public void testUpgrade05() throws Exception {
+ addUsersToUserManager(10, 11, 20, 21);
+
+ // First, migrate.
+ {
+ final OwnersSub owners = new OwnersSub();
+
+ createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test05/input.xml"));
+
+ owners.load();
+
+ // The legacy file should be removed.
+ assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+
+ assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
+
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
+
+ assertFalse(owners.hasDeviceOwner());
+ assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
+
+ assertTrue(owners.hasDeviceInitializer());
+ assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
+
+ assertNull(owners.getSystemUpdatePolicy());
+ assertEquals(0, owners.getProfileOwnerKeys().size());
+ }
+
+ // Then re-read and check.
+ {
+ final OwnersSub owners = new OwnersSub();
+ owners.load();
+
+ assertFalse(owners.hasDeviceOwner());
+ assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
+
+ assertTrue(owners.hasDeviceInitializer());
+ assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
+
+ assertNull(owners.getSystemUpdatePolicy());
+ assertEquals(0, owners.getProfileOwnerKeys().size());
+ }
+ }
+
+ public void testUpgrade06() throws Exception {
+ addUsersToUserManager(10, 11, 20, 21);
+
+ // First, migrate.
+ {
+ final OwnersSub owners = new OwnersSub();
+
+ createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test06/input.xml"));
+
+ owners.load();
+
+ // The legacy file should be removed.
+ assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+
+ assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
+
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(20).exists());
+
+ assertFalse(owners.hasDeviceOwner());
+ assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
+ assertFalse(owners.hasDeviceInitializer());
+ assertEquals(0, owners.getProfileOwnerKeys().size());
+
+ assertNotNull(owners.getSystemUpdatePolicy());
+ assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
+ }
+
+ // Then re-read and check.
+ {
+ final OwnersSub owners = new OwnersSub();
+ owners.load();
+
+ assertFalse(owners.hasDeviceOwner());
+ assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
+ assertFalse(owners.hasDeviceInitializer());
+ assertEquals(0, owners.getProfileOwnerKeys().size());
+
+ assertNotNull(owners.getSystemUpdatePolicy());
+ assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
+ }
+ }
+
+ public void testRemoveExistingFiles() throws Exception {
+ addUsersToUserManager(10, 11, 20, 21);
+
+ final OwnersSub owners = new OwnersSub();
+
+ // First, migrate to create new-style config files.
+ createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test04/input.xml"));
+
+ owners.load();
+
+ assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
+
+ assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
+ assertTrue(owners.getProfileOwnerFileWithTestOverride(10).exists());
+ assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists());
+
+ // Then clear all information and save.
+ owners.clearDeviceInitializer();
+ owners.clearDeviceOwner();
+ owners.clearSystemUpdatePolicy();
+ owners.removeProfileOwner(10);
+ owners.removeProfileOwner(11);
+
+ owners.writeDeviceOwner();
+ owners.writeProfileOwner(10);
+ owners.writeProfileOwner(11);
+ owners.writeProfileOwner(20);
+ owners.writeProfileOwner(21);
+
+ // Now all files should be removed.
+ assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
+ assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
+ }
+}
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..0c3f9da 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 {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2d31a78..8a1a553 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -206,8 +206,14 @@
*/
public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
+ /**
+ * Call sends responses through connection.
+ * @hide
+ */
+ public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
+
//******************************************************************************************
- // Next CAPABILITY value: 0x00004000
+ // Next CAPABILITY value: 0x00800000
//******************************************************************************************
/**
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 594e45a..4115756 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -248,8 +248,15 @@
*/
public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000;
+ /**
+ * Indicates that the connection itself wants to handle any sort of reply response, rather than
+ * relying on SMS.
+ * @hide
+ */
+ public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
+
//**********************************************************************************************
- // Next CAPABILITY value: 0x00400000
+ // Next CAPABILITY value: 0x00800000
//**********************************************************************************************
/**
@@ -388,6 +395,10 @@
if (can(capabilities, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
builder.append(" CAPABILITY_SINGLE_PARTY_CONFERENCE");
}
+ if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
+ builder.append(" CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION");
+ }
+
builder.append("]");
return builder.toString();
}
@@ -1787,6 +1798,13 @@
public void onReject() {}
/**
+ * Notifies ths Connection of a request reject with a message.
+ *
+ * @hide
+ */
+ public void onReject(String replyMessage) {}
+
+ /**
* Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
*/
public void onPostDialContinue(boolean proceed) {}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index f6ba53b..6223495 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -101,6 +101,7 @@
private static final int MSG_ANSWER_VIDEO = 17;
private static final int MSG_MERGE_CONFERENCE = 18;
private static final int MSG_SWAP_CONFERENCE = 19;
+ private static final int MSG_REJECT_WITH_MESSAGE = 20;
private static Connection sNullConnection;
@@ -168,6 +169,14 @@
}
@Override
+ public void rejectWithMessage(String callId, String message) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = message;
+ mHandler.obtainMessage(MSG_REJECT_WITH_MESSAGE, args).sendToTarget();
+ }
+
+ @Override
public void disconnect(String callId) {
mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
}
@@ -298,6 +307,15 @@
case MSG_REJECT:
reject((String) msg.obj);
break;
+ case MSG_REJECT_WITH_MESSAGE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ reject((String) args.arg1, (String) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
case MSG_DISCONNECT:
disconnect((String) msg.obj);
break;
@@ -685,6 +703,11 @@
findConnectionForAction(callId, "reject").onReject();
}
+ private void reject(String callId, String rejectWithMessage) {
+ Log.d(this, "reject %s with message", callId);
+ findConnectionForAction(callId, "reject").onReject(rejectWithMessage);
+ }
+
private void disconnect(String callId) {
Log.d(this, "disconnect %s", callId);
if (mConnectionById.containsKey(callId)) {
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index c2e8530..dd253cf 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -50,6 +50,8 @@
void reject(String callId);
+ void rejectWithMessage(String callId, String message);
+
void disconnect(String callId);
void hold(String callId);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index bfb7a50..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
@@ -274,6 +283,27 @@
= "carrier_use_ims_first_for_emergency_bool";
/**
+ * When IMS instant lettering is available for a carrier (see
+ * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the list of characters
+ * which may not be contained in messages. Should be specified as a regular expression suitable
+ * for use with {@link String#matches(String)}.
+ * @hide
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING =
+ "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
@@ -296,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. */
/**
@@ -354,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";
/**
@@ -378,6 +424,18 @@
public static final String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
/**
+ * Determine whether IMS apn can be shown.
+ * @hide
+ */
+ 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
@@ -419,6 +477,15 @@
public static final String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
public static final String KEY_MMS_USER_AGENT_STRING = "userAgent";
+ /**
+ * Determines whether the carrier supports making non-emergency phone calls while the phone is
+ * in emergency callback mode. Default value is {@code true}, meaning that non-emergency calls
+ * are allowed in emergency callback mode.
+ * @hide
+ */
+ public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL =
+ "allow_non_emergency_calls_in_ecm_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -439,6 +506,8 @@
sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
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);
@@ -459,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, "");
@@ -470,16 +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);
@@ -513,6 +587,7 @@
sDefaults.putString(KEY_MMS_UA_PROF_TAG_NAME_STRING, "x-wap-profile");
sDefaults.putString(KEY_MMS_UA_PROF_URL_STRING, "");
sDefaults.putString(KEY_MMS_USER_AGENT_STRING, "");
+ sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
}
/**
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 273cc93..b430340 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1457,10 +1457,15 @@
String result = null;
try {
PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
+ /**
+ * Need to reformat any local Korean phone numbers (when the user is in Korea) with
+ * country code to corresponding national format which would replace the leading
+ * +82 with 0.
+ */
if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) &&
- (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE))) {
- // Format local Korean phone numbers with country code to corresponding national
- // format which would replace the leading +82 with 0.
+ (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
+ (pn.getCountryCodeSource() ==
+ PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
} else {
result = util.formatInOriginalFormat(pn, defaultCountryIso);
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 0c5c557..2bfaf1b 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -185,6 +185,36 @@
case RILConstants.NETWORK_MODE_GLOBAL:
raf = GSM | WCDMA | CDMA | EVDO;
break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
+ raf = RAF_TD_SCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
+ raf = RAF_TD_SCDMA | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
+ raf = RAF_LTE | RAF_TD_SCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
+ raf = RAF_TD_SCDMA | GSM;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
+ raf = RAF_LTE | RAF_TD_SCDMA | GSM;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
+ raf = RAF_TD_SCDMA | GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
+ raf = RAF_LTE | RAF_TD_SCDMA | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
+ raf = RAF_LTE | RAF_TD_SCDMA | GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+ raf = RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+ raf = RAF_LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+ break;
default:
raf = RAF_UNKNOWN;
break;
@@ -248,6 +278,36 @@
case (GSM | WCDMA | CDMA | EVDO):
type = RILConstants.NETWORK_MODE_GLOBAL;
break;
+ case RAF_TD_SCDMA:
+ type = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
+ break;
+ case (RAF_TD_SCDMA | WCDMA):
+ type = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
+ break;
+ case (RAF_LTE | RAF_TD_SCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
+ break;
+ case (RAF_TD_SCDMA | GSM):
+ type = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
+ break;
+ case (RAF_LTE | RAF_TD_SCDMA | GSM):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
+ break;
+ case (RAF_TD_SCDMA | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
+ break;
+ case (RAF_LTE | RAF_TD_SCDMA | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
+ break;
+ case (RAF_LTE | RAF_TD_SCDMA | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
+ break;
+ case (RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+ break;
+ case (RAF_LTE | RAF_TD_SCDMA | RAF_LTE | CDMA | EVDO | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+ break;
default:
type = RILConstants.PREFERRED_NETWORK_MODE ;
break;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 80515cf..1337487 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -731,6 +731,9 @@
case RIL_RADIO_TECHNOLOGY_IWLAN:
rtString = "IWLAN";
break;
+ case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ rtString = "TD-SCDMA";
+ break;
default:
rtString = "Unexpected";
Rlog.w(LOG_TAG, "Unexpected radioTechnology=" + rt);
@@ -1068,6 +1071,8 @@
return TelephonyManager.NETWORK_TYPE_HSPAP;
case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
return TelephonyManager.NETWORK_TYPE_GSM;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
return TelephonyManager.NETWORK_TYPE_IWLAN;
default:
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index f02d109..f535e5d 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -68,6 +68,7 @@
private int mLteRsrq;
private int mLteRssnr;
private int mLteCqi;
+ private int mTdScdmaRscp;
private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
@@ -107,6 +108,7 @@
mLteRsrq = INVALID;
mLteRssnr = INVALID;
mLteCqi = INVALID;
+ mTdScdmaRscp = INVALID;
isGsm = true;
}
@@ -131,6 +133,7 @@
mLteRsrq = INVALID;
mLteRssnr = INVALID;
mLteCqi = INVALID;
+ mTdScdmaRscp = INVALID;
isGsm = gsmFlag;
}
@@ -143,6 +146,22 @@
int cdmaDbm, int cdmaEcio,
int evdoDbm, int evdoEcio, int evdoSnr,
int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+ int tdScdmaRscp, boolean gsmFlag) {
+ initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+ evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+ lteRsrq, lteRssnr, lteCqi, gsmFlag);
+ mTdScdmaRscp = tdScdmaRscp;
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+ int cdmaDbm, int cdmaEcio,
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
@@ -233,6 +252,7 @@
mLteRsrq = lteRsrq;
mLteRssnr = lteRssnr;
mLteCqi = lteCqi;
+ mTdScdmaRscp = INVALID;
isGsm = gsm;
if (DBG) log("initialize: " + toString());
}
@@ -253,6 +273,7 @@
mLteRsrq = s.mLteRsrq;
mLteRssnr = s.mLteRssnr;
mLteCqi = s.mLteCqi;
+ mTdScdmaRscp = s.mTdScdmaRscp;
isGsm = s.isGsm;
}
@@ -276,6 +297,7 @@
mLteRsrq = in.readInt();
mLteRssnr = in.readInt();
mLteCqi = in.readInt();
+ mTdScdmaRscp = in.readInt();
isGsm = (in.readInt() != 0);
}
@@ -302,7 +324,7 @@
ss.mLteRsrq = in.readInt();
ss.mLteRssnr = in.readInt();
ss.mLteCqi = in.readInt();
-
+ ss.mTdScdmaRscp = in.readInt();
return ss;
}
@@ -322,6 +344,7 @@
out.writeInt(mLteRsrq);
out.writeInt(mLteRssnr);
out.writeInt(mLteCqi);
+ out.writeInt(mTdScdmaRscp);
out.writeInt(isGsm ? 1 : 0);
}
@@ -377,6 +400,9 @@
mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID;
mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300)) ? mLteRssnr
: SignalStrength.INVALID;
+
+ mTdScdmaRscp = ((mTdScdmaRscp >= 25) && (mTdScdmaRscp <= 120))
+ ? -mTdScdmaRscp : SignalStrength.INVALID;
// Cqi no change
if (DBG) log("Signal after validate=" + this);
}
@@ -477,12 +503,15 @@
* while 4 represents a very strong signal strength.
*/
public int getLevel() {
- int level;
+ int level = 0;
if (isGsm) {
level = getLteLevel();
if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- level = getGsmLevel();
+ level = getTdScdmaLevel();
+ if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ level = getGsmLevel();
+ }
}
} else {
int cdmaLevel = getCdmaLevel();
@@ -508,10 +537,14 @@
* @hide
*/
public int getAsuLevel() {
- int asuLevel;
+ int asuLevel = 0;
if (isGsm) {
if (getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- asuLevel = getGsmAsuLevel();
+ if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ asuLevel = getGsmAsuLevel();
+ } else {
+ asuLevel = getTdScdmaAsuLevel();
+ }
} else {
asuLevel = getLteAsuLevel();
}
@@ -539,12 +572,16 @@
* @hide
*/
public int getDbm() {
- int dBm;
+ int dBm = INVALID;
if(isGsm()) {
dBm = getLteDbm();
if (dBm == INVALID) {
- dBm = getGsmDbm();
+ if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ dBm = getGsmDbm();
+ } else {
+ dBm = getTdScdmaDbm();
+ }
}
} else {
int cdmaDbm = getCdmaDbm();
@@ -849,6 +886,54 @@
}
/**
+ * @return get TD_SCDMA dbm
+ *
+ * @hide
+ */
+ public int getTdScdmaDbm() {
+ return this.mTdScdmaRscp;
+ }
+
+ /**
+ * Get TD-SCDMA as level 0..4
+ * Range : 25 to 120
+ * INT_MAX: 0x7FFFFFFF denotes invalid value
+ * Reference: 3GPP TS 25.123, section 9.1.1.1
+ *
+ * @hide
+ */
+ public int getTdScdmaLevel() {
+ final int tdScdmaDbm = getTdScdmaDbm();
+ int level;
+
+ if ((tdScdmaDbm > -25) || (tdScdmaDbm == SignalStrength.INVALID))
+ level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (tdScdmaDbm >= -49) level = SIGNAL_STRENGTH_GREAT;
+ else if (tdScdmaDbm >= -73) level = SIGNAL_STRENGTH_GOOD;
+ else if (tdScdmaDbm >= -97) level = SIGNAL_STRENGTH_MODERATE;
+ else if (tdScdmaDbm >= -120) level = SIGNAL_STRENGTH_POOR;
+ else level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ if (DBG) log("getTdScdmaLevel = " + level);
+ return level;
+ }
+
+ /**
+ * Get the TD-SCDMA signal level as an asu value.
+ *
+ * @hide
+ */
+ public int getTdScdmaAsuLevel() {
+ final int tdScdmaDbm = getTdScdmaDbm();
+ int tdScdmaAsuLevel;
+
+ if (tdScdmaDbm == INVALID) tdScdmaAsuLevel = 255;
+ else tdScdmaAsuLevel = tdScdmaDbm + 120;
+ if (DBG) log("TD-SCDMA Asu level: " + tdScdmaAsuLevel);
+ return tdScdmaAsuLevel;
+ }
+
+ /**
* @return hash code
*/
@Override
@@ -860,7 +945,7 @@
+ (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
+ (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
+ (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
- + (isGsm ? 1 : 0));
+ + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0));
}
/**
@@ -892,6 +977,7 @@
&& mLteRsrq == s.mLteRsrq
&& mLteRssnr == s.mLteRssnr
&& mLteCqi == s.mLteCqi
+ && mTdScdmaRscp == s.mTdScdmaRscp
&& isGsm == s.isGsm);
}
@@ -913,6 +999,7 @@
+ " " + mLteRsrq
+ " " + mLteRssnr
+ " " + mLteCqi
+ + " " + mTdScdmaRscp
+ " " + (isGsm ? "gsm|lte" : "cdma"));
}
@@ -935,6 +1022,7 @@
mLteRsrq = m.getInt("LteRsrq");
mLteRssnr = m.getInt("LteRssnr");
mLteCqi = m.getInt("LteCqi");
+ mTdScdmaRscp = m.getInt("TdScdma");
isGsm = m.getBoolean("isGsm");
}
@@ -957,6 +1045,7 @@
m.putInt("LteRsrq", mLteRsrq);
m.putInt("LteRssnr", mLteRssnr);
m.putInt("LteCqi", mLteCqi);
+ m.putInt("TdScdma", mTdScdmaRscp);
m.putBoolean("isGsm", Boolean.valueOf(isGsm));
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e104b38..f6e4bed 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1099,11 +1099,21 @@
case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
case RILConstants.NETWORK_MODE_LTE_WCDMA:
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
+ case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
+ case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
return PhoneConstants.PHONE_TYPE_GSM;
// Use CDMA Phone for the global mode including CDMA
case RILConstants.NETWORK_MODE_GLOBAL:
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
+ case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
return PhoneConstants.PHONE_TYPE_CDMA;
case RILConstants.NETWORK_MODE_LTE_ONLY:
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index a6a2658..23a69d1 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -71,7 +71,7 @@
* @param disabledFeatures features disabled as defined in com.android.ims.ImsConfig#FeatureConstants.
*/
void registrationFeatureCapabilityChanged(int serviceClass,
- out int[] enabledFeatures, out int[] disabledFeatures);
+ in int[] enabledFeatures, in int[] disabledFeatures);
/**
* Updates the application with the waiting voice message count.
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 8d48c86..7088be8 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -96,6 +96,16 @@
int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; /* LTE, CDMA, EvDo, GSM/WCDMA */
int NETWORK_MODE_LTE_ONLY = 11; /* LTE Only mode. */
int NETWORK_MODE_LTE_WCDMA = 12; /* LTE/WCDMA */
+ int NETWORK_MODE_TDSCDMA_ONLY = 13; /* TD-SCDMA only */
+ int NETWORK_MODE_TDSCDMA_WCDMA = 14; /* TD-SCDMA and WCDMA */
+ int NETWORK_MODE_LTE_TDSCDMA = 15; /* TD-SCDMA and LTE */
+ int NETWORK_MODE_TDSCDMA_GSM = 16; /* TD-SCDMA and GSM */
+ int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; /* TD-SCDMA,GSM and LTE */
+ int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; /* TD-SCDMA, GSM/WCDMA */
+ int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; /* TD-SCDMA, WCDMA and LTE */
+ int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; /* TD-SCDMA, GSM/WCDMA and LTE */
+ int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; /*TD-SCDMA,EvDo,CDMA,GSM/WCDMA*/
+ int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; /* TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo */
int PREFERRED_NETWORK_MODE = SystemProperties.getInt("ro.telephony.default_network",
NETWORK_MODE_WCDMA_PREF);
diff --git a/tests/SurfaceComposition/Android.mk b/tests/SurfaceComposition/Android.mk
new file mode 100644
index 0000000..95f69f1
--- /dev/null
+++ b/tests/SurfaceComposition/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SurfaceComposition
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tests/SurfaceComposition/AndroidManifest.xml b/tests/SurfaceComposition/AndroidManifest.xml
new file mode 100644
index 0000000..4c0a9b6
--- /dev/null
+++ b/tests/SurfaceComposition/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?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.surfacecomposition">
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <application android:theme="@style/noeffects">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.surfacecomposition.SurfaceCompositionMeasuringActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="android.surfacecomposition">
+ </instrumentation>
+</manifest>
diff --git a/tests/SurfaceComposition/res/values/themes.xml b/tests/SurfaceComposition/res/values/themes.xml
new file mode 100644
index 0000000..254d707
--- /dev/null
+++ b/tests/SurfaceComposition/res/values/themes.xml
@@ -0,0 +1,25 @@
+<?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>
+ <style name="noeffects" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:fadingEdge">none</item>
+ <item name="android:windowContentTransitions">false</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ </style>
+</resources>
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/CustomLayout.java b/tests/SurfaceComposition/src/android/surfacecomposition/CustomLayout.java
new file mode 100644
index 0000000..d626f10
--- /dev/null
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/CustomLayout.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 android.surfacecomposition;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class CustomLayout extends ViewGroup {
+ public CustomLayout(Context context) {
+ super(context);
+ }
+
+ public static class LayoutParams extends ViewGroup.LayoutParams {
+ private int mLeft, mTop, mRight, mBottom;
+
+ public LayoutParams(int left, int top, int right, int bottom) {
+ super(0, 0);
+ mLeft = left;
+ mTop = top;
+ mRight = right;
+ mBottom = bottom;
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ CustomLayout.LayoutParams lp = (CustomLayout.LayoutParams) child.getLayoutParams();
+ child.layout(lp.mLeft, lp.mTop, lp.mRight, lp.mBottom);
+ }
+ }
+}
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/CustomSurfaceView.java b/tests/SurfaceComposition/src/android/surfacecomposition/CustomSurfaceView.java
new file mode 100644
index 0000000..0430662
--- /dev/null
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/CustomSurfaceView.java
@@ -0,0 +1,243 @@
+/*
+ * 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.surfacecomposition;
+
+import java.util.Random;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * This provides functionality to measure Surface update frame rate. The idea is to
+ * constantly invalidates Surface in a separate thread. Lowest possible way is to
+ * use SurfaceView which works with Surface. This gives a very small overhead
+ * and very close to Android internals. Note, that lockCanvas is blocking
+ * methods and it returns once SurfaceFlinger consumes previous buffer. This
+ * gives the change to measure real performance of Surface compositor.
+ */
+public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+ private final static long DURATION_TO_WARMUP_MS = 50;
+ private final static long DURATION_TO_MEASURE_ROUGH_MS = 500;
+ private final static long DURATION_TO_MEASURE_PRECISE_MS = 3000;
+ private final static Random mRandom = new Random();
+
+ private final Object mSurfaceLock = new Object();
+ private Surface mSurface;
+ private boolean mDrawNameOnReady = true;
+ private boolean mSurfaceWasChanged = false;
+ private String mName;
+ private Canvas mCanvas;
+
+ class ValidateThread extends Thread {
+ private double mFPS = 0.0f;
+ // Used to support early exit and prevent long computation.
+ private double mBadFPS;
+ private double mPerfectFPS;
+
+ ValidateThread(double badFPS, double perfectFPS) {
+ mBadFPS = badFPS;
+ mPerfectFPS = perfectFPS;
+ }
+
+ public void run() {
+ long startTime = System.currentTimeMillis();
+ while (System.currentTimeMillis() - startTime < DURATION_TO_WARMUP_MS) {
+ invalidateSurface(false);
+ }
+
+ startTime = System.currentTimeMillis();
+ long endTime;
+ int frameCnt = 0;
+ while (true) {
+ invalidateSurface(false);
+ endTime = System.currentTimeMillis();
+ ++frameCnt;
+ mFPS = (double)frameCnt * 1000.0 / (endTime - startTime);
+ if ((endTime - startTime) >= DURATION_TO_MEASURE_ROUGH_MS) {
+ // Test if result looks too bad or perfect and stop early.
+ if (mFPS <= mBadFPS || mFPS >= mPerfectFPS) {
+ break;
+ }
+ }
+ if ((endTime - startTime) >= DURATION_TO_MEASURE_PRECISE_MS) {
+ break;
+ }
+ }
+ }
+
+ public double getFPS() {
+ return mFPS;
+ }
+ }
+
+ public CustomSurfaceView(Context context, String name) {
+ super(context);
+ mName = name;
+ getHolder().addCallback(this);
+ }
+
+ public void setMode(int pixelFormat, boolean drawNameOnReady) {
+ mDrawNameOnReady = drawNameOnReady;
+ getHolder().setFormat(pixelFormat);
+ }
+
+ public void acquireCanvas() {
+ synchronized (mSurfaceLock) {
+ if (mCanvas != null) {
+ throw new RuntimeException("Surface canvas was already acquired.");
+ }
+ if (mSurface != null) {
+ mCanvas = mSurface.lockCanvas(null);
+ }
+ }
+ }
+
+ public void releaseCanvas() {
+ synchronized (mSurfaceLock) {
+ if (mCanvas != null) {
+ if (mSurface == null) {
+ throw new RuntimeException(
+ "Surface was destroyed but canvas was not released.");
+ }
+ mSurface.unlockCanvasAndPost(mCanvas);
+ mCanvas = null;
+ }
+ }
+ }
+
+ /**
+ * Invalidate surface.
+ */
+ private void invalidateSurface(boolean drawSurfaceId) {
+ synchronized (mSurfaceLock) {
+ if (mSurface != null) {
+ Canvas canvas = mSurface.lockCanvas(null);
+ // Draw surface name for debug purpose only. This does not affect the test
+ // because it is drawn only during allocation.
+ if (drawSurfaceId) {
+ int textSize = canvas.getHeight() / 24;
+ Paint paint = new Paint();
+ paint.setTextSize(textSize);
+ int textWidth = (int)(paint.measureText(mName) + 0.5f);
+ int x = mRandom.nextInt(canvas.getWidth() - textWidth);
+ int y = textSize + mRandom.nextInt(canvas.getHeight() - textSize);
+ // Create effect of fog to visually control correctness of composition.
+ paint.setColor(0xFFFF8040);
+ canvas.drawARGB(32, 255, 255, 255);
+ canvas.drawText(mName, x, y, paint);
+ }
+ mSurface.unlockCanvasAndPost(canvas);
+ }
+ }
+ }
+
+ /**
+ * Wait until surface is created and ready to use or return immediately if surface
+ * already exists.
+ */
+ public void waitForSurfaceReady() {
+ synchronized (mSurfaceLock) {
+ if (mSurface == null) {
+ try {
+ mSurfaceLock.wait(5000);
+ } catch(InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (mSurface == null)
+ throw new RuntimeException("Surface is not ready.");
+ mSurfaceWasChanged = false;
+ }
+ }
+
+ /**
+ * Wait until surface is destroyed or return immediately if surface does not exist.
+ */
+ public void waitForSurfaceDestroyed() {
+ synchronized (mSurfaceLock) {
+ if (mSurface != null) {
+ try {
+ mSurfaceLock.wait(5000);
+ } catch(InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (mSurface != null)
+ throw new RuntimeException("Surface still exists.");
+ mSurfaceWasChanged = false;
+ }
+ }
+
+ /**
+ * Validate that surface has not been changed since waitForSurfaceReady or
+ * waitForSurfaceDestroyed.
+ */
+ public void validateSurfaceNotChanged() {
+ synchronized (mSurfaceLock) {
+ if (mSurfaceWasChanged) {
+ throw new RuntimeException("Surface was changed during the test execution.");
+ }
+ }
+ }
+
+ public double measureFPS(double badFPS, double perfectFPS) {
+ try {
+ ValidateThread validateThread = new ValidateThread(badFPS, perfectFPS);
+ validateThread.start();
+ validateThread.join();
+ return validateThread.getFPS();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ synchronized (mSurfaceLock) {
+ mSurfaceWasChanged = true;
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ // This method is always called at least once, after surfaceCreated.
+ synchronized (mSurfaceLock) {
+ mSurface = holder.getSurface();
+ // We only need to invalidate the surface for the compositor performance test so that
+ // it gets included in the composition process. For allocation performance we
+ // don't need to invalidate surface and this allows us to remove non-necessary
+ // surface invalidation from the test.
+ if (mDrawNameOnReady) {
+ invalidateSurface(true);
+ }
+ mSurfaceWasChanged = true;
+ mSurfaceLock.notify();
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ synchronized (mSurfaceLock) {
+ mSurface = null;
+ mSurfaceWasChanged = true;
+ mSurfaceLock.notify();
+ }
+ }
+}
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/MemoryAccessTask.java b/tests/SurfaceComposition/src/android/surfacecomposition/MemoryAccessTask.java
new file mode 100644
index 0000000..c716dae
--- /dev/null
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/MemoryAccessTask.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 android.surfacecomposition;
+
+import android.util.Log;
+
+/**
+ * This task will simulate CPU activity by consuming memory bandwidth from the system.
+ * Note: On most system the CPU and GPU will share the same memory.
+ */
+public class MemoryAccessTask {
+ private final static String TAG = "MemoryAccessTask";
+ private final static int BUFFER_SIZE = 32 * 1024 * 1024;
+ private final static int BUFFER_STEP = 256;
+ private boolean mStopRequested;
+ private WorkThread mThread;
+ private final Object mLock = new Object();
+
+ public class WorkThread extends Thread {
+ public void run() {
+ byte[] memory = new byte[BUFFER_SIZE];
+ while (true) {
+ synchronized (mLock) {
+ if (mStopRequested) {
+ break;
+ }
+ }
+ long result = 0;
+ for (int index = 0; index < BUFFER_SIZE; index += BUFFER_STEP) {
+ result += ++memory[index];
+ }
+ Log.v(TAG, "Processing...:" + result);
+ }
+ }
+ }
+
+ public void start() {
+ if (mThread != null) {
+ throw new RuntimeException("Work thread is already started");
+ }
+ mStopRequested = false;
+ mThread = new WorkThread();
+ mThread.start();
+ }
+
+ public void stop() {
+ if (mThread != null) {
+ synchronized (mLock) {
+ mStopRequested = true;
+ }
+ try {
+ mThread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
new file mode 100644
index 0000000..e3e1d34
--- /dev/null
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
@@ -0,0 +1,602 @@
+/*
+ * 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.surfacecomposition;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.MemoryInfo;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+/**
+ * This activity is designed to measure peformance scores of Android surfaces.
+ * It can work in two modes. In first mode functionality of this activity is
+ * invoked from Cts test (SurfaceCompositionTest). This activity can also be
+ * used in manual mode as a normal app. Different pixel formats are supported.
+ *
+ * measureCompositionScore(pixelFormat)
+ * This test measures surface compositor performance which shows how many
+ * surfaces of specific format surface compositor can combine without dropping
+ * frames. We allow one dropped frame per half second.
+ *
+ * measureAllocationScore(pixelFormat)
+ * This test measures surface allocation/deallocation performance. It shows
+ * how many surface lifecycles (creation, destruction) can be done per second.
+ *
+ * In manual mode, which activated by pressing button 'Compositor speed' or
+ * 'Allocator speed', all possible pixel format are tested and combined result
+ * is displayed in text view. Additional system information such as memory
+ * status, display size and surface format is also displayed and regulary
+ * updated.
+ */
+public class SurfaceCompositionMeasuringActivity extends Activity implements OnClickListener {
+ private final static int MIN_NUMBER_OF_SURFACES = 15;
+ private final static int MAX_NUMBER_OF_SURFACES = 40;
+ private final static int WARM_UP_ALLOCATION_CYCLES = 2;
+ private final static int MEASURE_ALLOCATION_CYCLES = 5;
+ private final static int TEST_COMPOSITOR = 1;
+ private final static int TEST_ALLOCATION = 2;
+ private final static float MIN_REFRESH_RATE_SUPPORTED = 50.0f;
+
+ private final static DecimalFormat DOUBLE_FORMAT = new DecimalFormat("#.00");
+ // Possible selection in pixel format selector.
+ private final static int[] PIXEL_FORMATS = new int[] {
+ PixelFormat.TRANSLUCENT,
+ PixelFormat.TRANSPARENT,
+ PixelFormat.OPAQUE,
+ PixelFormat.RGBA_8888,
+ PixelFormat.RGBX_8888,
+ PixelFormat.RGB_888,
+ PixelFormat.RGB_565,
+ };
+
+
+ private List<CustomSurfaceView> mViews = new ArrayList<CustomSurfaceView>();
+ private Button mMeasureCompositionButton;
+ private Button mMeasureAllocationButton;
+ private Spinner mPixelFormatSelector;
+ private TextView mResultView;
+ private TextView mSystemInfoView;
+ private final Object mLockResumed = new Object();
+ private boolean mResumed;
+
+ // Drop one frame per half second.
+ // TODO(khmel)
+ // Add a feature flag and set the target FPS dependent on the target system as e.g.:
+ // 59FPS for MULTI_WINDOW and 54 otherwise (to satisfy the default lax Android requirements).
+ private double mRefreshRate;
+ private double mTargetFPS;
+
+ private int mWidth;
+ private int mHeight;
+
+ class CompositorScore {
+ double mSurfaces;
+ double mBitrate;
+
+ @Override
+ public String toString() {
+ return DOUBLE_FORMAT.format(mSurfaces) + " surfaces. " +
+ "Bitrate: " + getReadableMemory((long)mBitrate) + "/s";
+ }
+ }
+
+ /**
+ * Measure performance score.
+ *
+ * @return biggest possible number of visible surfaces which surface
+ * compositor can handle.
+ */
+ public CompositorScore measureCompositionScore(int pixelFormat) {
+ waitForActivityResumed();
+ //MemoryAccessTask memAccessTask = new MemoryAccessTask();
+ //memAccessTask.start();
+ // Destroy any active surface.
+ configureSurfacesAndWait(0, pixelFormat, false);
+ CompositorScore score = new CompositorScore();
+ score.mSurfaces = measureCompositionScore(new Measurement(0, 60.0),
+ new Measurement(mViews.size() + 1, 0.0f), pixelFormat);
+ // Assume 32 bits per pixel.
+ score.mBitrate = score.mSurfaces * mTargetFPS * mWidth * mHeight * 4.0;
+ //memAccessTask.stop();
+ return score;
+ }
+
+ static class AllocationScore {
+ double mMedian;
+ double mMin;
+ double mMax;
+
+ @Override
+ public String toString() {
+ return DOUBLE_FORMAT.format(mMedian) + " (min:" + DOUBLE_FORMAT.format(mMin) +
+ ", max:" + DOUBLE_FORMAT.format(mMax) + ") surface allocations per second";
+ }
+ }
+
+ public AllocationScore measureAllocationScore(int pixelFormat) {
+ waitForActivityResumed();
+ AllocationScore score = new AllocationScore();
+ for (int i = 0; i < MEASURE_ALLOCATION_CYCLES + WARM_UP_ALLOCATION_CYCLES; ++i) {
+ long time1 = System.currentTimeMillis();
+ configureSurfacesAndWait(MIN_NUMBER_OF_SURFACES, pixelFormat, false);
+ acquireSurfacesCanvas();
+ long time2 = System.currentTimeMillis();
+ releaseSurfacesCanvas();
+ configureSurfacesAndWait(0, pixelFormat, false);
+ // Give SurfaceFlinger some time to rebuild the layer stack and release the buffers.
+ try {
+ Thread.sleep(500);
+ } catch(InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (i < WARM_UP_ALLOCATION_CYCLES) {
+ // This is warm-up cycles, ignore result so far.
+ continue;
+ }
+ double speed = MIN_NUMBER_OF_SURFACES * 1000.0 / (time2 - time1);
+ score.mMedian += speed / MEASURE_ALLOCATION_CYCLES;
+ if (i == WARM_UP_ALLOCATION_CYCLES) {
+ score.mMin = speed;
+ score.mMax = speed;
+ } else {
+ score.mMin = Math.min(score.mMin, speed);
+ score.mMax = Math.max(score.mMax, speed);
+ }
+ }
+
+ return score;
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == mMeasureCompositionButton) {
+ doTest(TEST_COMPOSITOR);
+ } else if (view == mMeasureAllocationButton) {
+ doTest(TEST_ALLOCATION);
+ }
+ }
+
+ private void doTest(final int test) {
+ enableControls(false);
+ final int pixelFormat = PIXEL_FORMATS[mPixelFormatSelector.getSelectedItemPosition()];
+ new Thread() {
+ public void run() {
+ final StringBuffer sb = new StringBuffer();
+ switch (test) {
+ case TEST_COMPOSITOR: {
+ sb.append("Compositor score:");
+ CompositorScore score = measureCompositionScore(pixelFormat);
+ sb.append("\n " + getPixelFormatInfo(pixelFormat) + ":" +
+ score + ".");
+ }
+ break;
+ case TEST_ALLOCATION: {
+ sb.append("Allocation score:");
+ AllocationScore score = measureAllocationScore(pixelFormat);
+ sb.append("\n " + getPixelFormatInfo(pixelFormat) + ":" +
+ score + ".");
+ }
+ break;
+ }
+ runOnUiThreadAndWait(new Runnable() {
+ public void run() {
+ mResultView.setText(sb.toString());
+ enableControls(true);
+ updateSystemInfo(pixelFormat);
+ }
+ });
+ }
+ }.start();
+ }
+
+ /**
+ * Wait until activity is resumed.
+ */
+ public void waitForActivityResumed() {
+ synchronized (mLockResumed) {
+ if (!mResumed) {
+ try {
+ mLockResumed.wait(10000);
+ } catch (InterruptedException e) {
+ }
+ }
+ if (!mResumed) {
+ throw new RuntimeException("Activity was not resumed");
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ detectRefreshRate();
+
+ // To layouts in parent. First contains list of Surfaces and second
+ // controls. Controls stay on top.
+ RelativeLayout rootLayout = new RelativeLayout(this);
+ rootLayout.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ CustomLayout layout = new CustomLayout(this);
+ layout.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ Rect rect = new Rect();
+ getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
+ mWidth = rect.right;
+ mHeight = rect.bottom;
+ long maxMemoryPerSurface = roundToNextPowerOf2(mWidth) * roundToNextPowerOf2(mHeight) * 4;
+ // Use 75% of available memory.
+ int surfaceCnt = (int)((getMemoryInfo().availMem * 3) / (4 * maxMemoryPerSurface));
+ if (surfaceCnt < MIN_NUMBER_OF_SURFACES) {
+ throw new RuntimeException("Not enough memory to allocate " +
+ MIN_NUMBER_OF_SURFACES + " surfaces.");
+ }
+ if (surfaceCnt > MAX_NUMBER_OF_SURFACES) {
+ surfaceCnt = MAX_NUMBER_OF_SURFACES;
+ }
+
+ LinearLayout controlLayout = new LinearLayout(this);
+ controlLayout.setOrientation(LinearLayout.VERTICAL);
+ controlLayout.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ mMeasureCompositionButton = createButton("Compositor speed.", controlLayout);
+ mMeasureAllocationButton = createButton("Allocation speed", controlLayout);
+
+ String[] pixelFomats = new String[PIXEL_FORMATS.length];
+ for (int i = 0; i < pixelFomats.length; ++i) {
+ pixelFomats[i] = getPixelFormatInfo(PIXEL_FORMATS[i]);
+ }
+ mPixelFormatSelector = new Spinner(this);
+ ArrayAdapter<String> pixelFormatSelectorAdapter =
+ new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, pixelFomats);
+ pixelFormatSelectorAdapter.setDropDownViewResource(
+ android.R.layout.simple_spinner_dropdown_item);
+ mPixelFormatSelector.setAdapter(pixelFormatSelectorAdapter);
+ mPixelFormatSelector.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ controlLayout.addView(mPixelFormatSelector);
+
+ mResultView = new TextView(this);
+ mResultView.setBackgroundColor(0);
+ mResultView.setText("Press button to start test.");
+ mResultView.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ controlLayout.addView(mResultView);
+
+ mSystemInfoView = new TextView(this);
+ mSystemInfoView.setBackgroundColor(0);
+ mSystemInfoView.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ controlLayout.addView(mSystemInfoView);
+
+ for (int i = 0; i < surfaceCnt; ++i) {
+ CustomSurfaceView view = new CustomSurfaceView(this, "Surface:" + i);
+ // Create all surfaces overlapped in order to prevent SurfaceFlinger
+ // to filter out surfaces by optimization in case surface is opaque.
+ // In case surface is transparent it will be drawn anyway. Note that first
+ // surface covers whole screen and must stand below other surfaces. Z order of
+ // layers is not predictable and there is only one way to force first
+ // layer to be below others is to mark it as media and all other layers
+ // to mark as media overlay.
+ if (i == 0) {
+ view.setLayoutParams(new CustomLayout.LayoutParams(0, 0, mWidth, mHeight));
+ view.setZOrderMediaOverlay(false);
+ } else {
+ // Z order of other layers is not predefined so make offset on x and reverse
+ // offset on y to make sure that surface is visible in any layout.
+ int x = i;
+ int y = (surfaceCnt - i);
+ view.setLayoutParams(new CustomLayout.LayoutParams(x, y, x + mWidth, y + mHeight));
+ view.setZOrderMediaOverlay(true);
+ }
+ view.setVisibility(View.INVISIBLE);
+ layout.addView(view);
+ mViews.add(view);
+ }
+
+ rootLayout.addView(layout);
+ rootLayout.addView(controlLayout);
+
+ setContentView(rootLayout);
+ }
+
+ private Button createButton(String caption, LinearLayout layout) {
+ Button button = new Button(this);
+ button.setText(caption);
+ button.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ button.setOnClickListener(this);
+ layout.addView(button);
+ return button;
+ }
+
+ private void enableControls(boolean enabled) {
+ mMeasureCompositionButton.setEnabled(enabled);
+ mMeasureAllocationButton.setEnabled(enabled);
+ mPixelFormatSelector.setEnabled(enabled);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ updateSystemInfo(PixelFormat.UNKNOWN);
+
+ synchronized (mLockResumed) {
+ mResumed = true;
+ mLockResumed.notifyAll();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ synchronized (mLockResumed) {
+ mResumed = false;
+ }
+ }
+
+ class Measurement {
+ Measurement(int surfaceCnt, double fps) {
+ mSurfaceCnt = surfaceCnt;
+ mFPS = fps;
+ }
+
+ public final int mSurfaceCnt;
+ public final double mFPS;
+ }
+
+ private double measureCompositionScore(Measurement ok, Measurement fail, int pixelFormat) {
+ if (ok.mSurfaceCnt + 1 == fail.mSurfaceCnt) {
+ // Interpolate result.
+ double fraction = (mTargetFPS - fail.mFPS) / (ok.mFPS - fail.mFPS);
+ return ok.mSurfaceCnt + fraction;
+ }
+
+ int medianSurfaceCnt = (ok.mSurfaceCnt + fail.mSurfaceCnt) / 2;
+ Measurement median = new Measurement(medianSurfaceCnt,
+ measureFPS(medianSurfaceCnt, pixelFormat));
+
+ if (median.mFPS >= mTargetFPS) {
+ return measureCompositionScore(median, fail, pixelFormat);
+ } else {
+ return measureCompositionScore(ok, median, pixelFormat);
+ }
+ }
+
+ private double measureFPS(int surfaceCnt, int pixelFormat) {
+ configureSurfacesAndWait(surfaceCnt, pixelFormat, true);
+ // At least one view is visible and it is enough to update only
+ // one overlapped surface in order to force SurfaceFlinger to send
+ // all surfaces to compositor.
+ double fps = mViews.get(0).measureFPS(mRefreshRate * 0.8, mRefreshRate * 0.999);
+
+ // Make sure that surface configuration was not changed.
+ validateSurfacesNotChanged();
+
+ return fps;
+ }
+
+ private void waitForSurfacesConfigured(final int pixelFormat) {
+ for (int i = 0; i < mViews.size(); ++i) {
+ CustomSurfaceView view = mViews.get(i);
+ if (view.getVisibility() == View.VISIBLE) {
+ view.waitForSurfaceReady();
+ } else {
+ view.waitForSurfaceDestroyed();
+ }
+ }
+ runOnUiThreadAndWait(new Runnable() {
+ @Override
+ public void run() {
+ updateSystemInfo(pixelFormat);
+ }
+ });
+ }
+
+ private void validateSurfacesNotChanged() {
+ for (int i = 0; i < mViews.size(); ++i) {
+ CustomSurfaceView view = mViews.get(i);
+ view.validateSurfaceNotChanged();
+ }
+ }
+
+ private void configureSurfaces(int surfaceCnt, int pixelFormat, boolean invalidate) {
+ for (int i = 0; i < mViews.size(); ++i) {
+ CustomSurfaceView view = mViews.get(i);
+ if (i < surfaceCnt) {
+ view.setMode(pixelFormat, invalidate);
+ view.setVisibility(View.VISIBLE);
+ } else {
+ view.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ private void configureSurfacesAndWait(final int surfaceCnt, final int pixelFormat,
+ final boolean invalidate) {
+ runOnUiThreadAndWait(new Runnable() {
+ @Override
+ public void run() {
+ configureSurfaces(surfaceCnt, pixelFormat, invalidate);
+ }
+ });
+ waitForSurfacesConfigured(pixelFormat);
+ }
+
+ private void acquireSurfacesCanvas() {
+ for (int i = 0; i < mViews.size(); ++i) {
+ CustomSurfaceView view = mViews.get(i);
+ view.acquireCanvas();
+ }
+ }
+
+ private void releaseSurfacesCanvas() {
+ for (int i = 0; i < mViews.size(); ++i) {
+ CustomSurfaceView view = mViews.get(i);
+ view.releaseCanvas();
+ }
+ }
+
+ private static String getReadableMemory(long bytes) {
+ long unit = 1024;
+ if (bytes < unit) {
+ return bytes + " B";
+ }
+ int exp = (int) (Math.log(bytes) / Math.log(unit));
+ return String.format("%.1f %sB", bytes / Math.pow(unit, exp),
+ "KMGTPE".charAt(exp-1));
+ }
+
+ private MemoryInfo getMemoryInfo() {
+ ActivityManager activityManager = (ActivityManager)
+ getSystemService(ACTIVITY_SERVICE);
+ MemoryInfo memInfo = new MemoryInfo();
+ activityManager.getMemoryInfo(memInfo);
+ return memInfo;
+ }
+
+ private void updateSystemInfo(int pixelFormat) {
+ int visibleCnt = 0;
+ for (int i = 0; i < mViews.size(); ++i) {
+ if (mViews.get(i).getVisibility() == View.VISIBLE) {
+ ++visibleCnt;
+ }
+ }
+
+ MemoryInfo memInfo = getMemoryInfo();
+ String info = "Available " +
+ getReadableMemory(memInfo.availMem) + " from " +
+ getReadableMemory(memInfo.totalMem) + ".\nVisible " +
+ visibleCnt + " from " + mViews.size() + " " +
+ getPixelFormatInfo(pixelFormat) + " surfaces.\n" +
+ "View size: " + mWidth + "x" + mHeight +
+ ". Refresh rate: " + DOUBLE_FORMAT.format(mRefreshRate) + ".";
+ mSystemInfoView.setText(info);
+ }
+
+ private void detectRefreshRate() {
+ WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
+ mRefreshRate = wm.getDefaultDisplay().getRefreshRate();
+ if (mRefreshRate < MIN_REFRESH_RATE_SUPPORTED)
+ throw new RuntimeException("Unsupported display refresh rate: " + mRefreshRate);
+ mTargetFPS = mRefreshRate - 2.0f;
+ }
+
+ private int roundToNextPowerOf2(int value) {
+ --value;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ return value + 1;
+ }
+
+ public static String getPixelFormatInfo(int pixelFormat) {
+ switch (pixelFormat) {
+ case PixelFormat.TRANSLUCENT:
+ return "TRANSLUCENT";
+ case PixelFormat.TRANSPARENT:
+ return "TRANSPARENT";
+ case PixelFormat.OPAQUE:
+ return "OPAQUE";
+ case PixelFormat.RGBA_8888:
+ return "RGBA_8888";
+ case PixelFormat.RGBX_8888:
+ return "RGBX_8888";
+ case PixelFormat.RGB_888:
+ return "RGB_888";
+ case PixelFormat.RGB_565:
+ return "RGB_565";
+ default:
+ return "PIX.FORMAT:" + pixelFormat;
+ }
+ }
+
+ /**
+ * A helper that executes a task in the UI thread and waits for its completion.
+ *
+ * @param task - task to execute.
+ */
+ private void runOnUiThreadAndWait(Runnable task) {
+ new UIExecutor(task);
+ }
+
+ class UIExecutor implements Runnable {
+ private final Object mLock = new Object();
+ private Runnable mTask;
+ private boolean mDone = false;
+
+ UIExecutor(Runnable task) {
+ mTask = task;
+ mDone = false;
+ runOnUiThread(this);
+ synchronized (mLock) {
+ while (!mDone) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public void run() {
+ mTask.run();
+ synchronized (mLock) {
+ mDone = true;
+ mLock.notify();
+ }
+ }
+ }
+}
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
new file mode 100644
index 0000000..6e9e739
--- /dev/null
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.surfacecomposition;
+
+import android.graphics.PixelFormat;
+import android.surfacecomposition.SurfaceCompositionMeasuringActivity.AllocationScore;
+import android.surfacecomposition.SurfaceCompositionMeasuringActivity.CompositorScore;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+public class SurfaceCompositionTest extends
+ ActivityInstrumentationTestCase2<SurfaceCompositionMeasuringActivity> {
+ private final static String TAG = "SurfaceCompositionTest";
+
+ // Pass threshold for major pixel formats.
+ private final static int[] TEST_PIXEL_FORMATS = new int[] {
+ PixelFormat.TRANSLUCENT,
+ PixelFormat.OPAQUE,
+ };
+
+ // Based on Nexus 9 performance which is usually < 9.0.
+ private final static double[] MIN_ACCEPTED_COMPOSITION_SCORE = new double[] {
+ 8.0,
+ 8.0,
+ };
+
+ // Based on Nexus 6 performance which is usually < 28.0.
+ private final static double[] MIN_ACCEPTED_ALLOCATION_SCORE = new double[] {
+ 20.0,
+ 20.0,
+ };
+
+ public SurfaceCompositionTest() {
+ super(SurfaceCompositionMeasuringActivity.class);
+ }
+
+ private void testRestoreContexts() {
+ }
+
+ @SmallTest
+ public void testSurfaceCompositionPerformance() {
+ for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) {
+ int pixelFormat = TEST_PIXEL_FORMATS[i];
+ String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat);
+ CompositorScore score = getActivity().measureCompositionScore(pixelFormat);
+ Log.i(TAG, "testSurfaceCompositionPerformance(" + formatName + ") = " + score);
+ assertTrue("Device does not support surface(" + formatName + ") composition " +
+ "performance score. " + score.mSurfaces + " < " +
+ MIN_ACCEPTED_COMPOSITION_SCORE[i] + ".",
+ score.mSurfaces >= MIN_ACCEPTED_COMPOSITION_SCORE[i]);
+ }
+ }
+
+ @SmallTest
+ public void testSurfaceAllocationPerformance() {
+ for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) {
+ int pixelFormat = TEST_PIXEL_FORMATS[i];
+ String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat);
+ AllocationScore score = getActivity().measureAllocationScore(pixelFormat);
+ Log.i(TAG, "testSurfaceAllocationPerformance(" + formatName + ") = " + score);
+ assertTrue("Device does not support surface(" + formatName + ") allocation " +
+ "performance score. " + score.mMedian + " < " +
+ MIN_ACCEPTED_ALLOCATION_SCORE[i] + ".",
+ score.mMedian >= MIN_ACCEPTED_ALLOCATION_SCORE[i]);
+ }
+ }
+}
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 1e5b117..0e678cd 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -10,14 +10,21 @@
# regressions are reflected in test data
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/res \
- frameworks/support/v7/appcompat/res
+ frameworks/support/v7/appcompat/res \
+ frameworks/support/v7/cardview/res \
+ frameworks/support/v7/recyclerview/res
LOCAL_AAPT_FLAGS := \
- --extra-packages android.support.v7.appcompat
+ --auto-add-overlay \
+ --extra-packages android.support.v7.appcompat \
+ --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-appcompat \
+ 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 6677e0d..7b4f01c 100644
--- a/tests/UiBench/AndroidManifest.xml
+++ b/tests/UiBench/AndroidManifest.xml
@@ -14,11 +14,13 @@
~ limitations under the License
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.uibench">
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.test.uibench">
<application
android:allowBackup="false"
- android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
+ android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
+ tools:ignore="MissingApplicationIcon">
<uses-library android:name="android.test.runner" />
<!-- Root navigation activity -->
@@ -32,10 +34,18 @@
</intent-filter>
</activity>
- <!-- Tests -->
+ <!-- General -->
+ <activity
+ android:name=".DialogListActivity"
+ android:label="General/Dialog List" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
<activity
android:name=".GlTextureViewActivity"
- android:label="Microbenchmarks/GL TextureView" >
+ android:label="General/GL TextureView" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.uibench.TEST" />
@@ -43,7 +53,15 @@
</activity>
<activity
android:name=".FullscreenOverdrawActivity"
- android:label="Microbenchmarks/Fullscreen Overdraw" >
+ android:label="General/Fullscreen Overdraw" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".InvalidateActivity"
+ android:label="General/Invalidate" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.uibench.TEST" />
@@ -51,7 +69,7 @@
</activity>
<activity
android:name=".TrivialAnimationActivity"
- android:label="Microbenchmarks/Trivial Animation" >
+ android:label="General/Trivial Animation" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.uibench.TEST" />
@@ -59,7 +77,85 @@
</activity>
<activity
android:name=".TrivialListActivity"
- android:label="Microbenchmarks/Trivial ListView" >
+ android:label="General/Trivial ListView" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <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
+ android:name=".BitmapUploadActivity"
+ android:label="Rendering/Bitmap Upload" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".ShadowGridActivity"
+ android:label="Rendering/Shadow Grid" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+
+ <!-- Inflation -->
+ <activity
+ android:name=".InflatingListActivity"
+ android:label="Inflation/Inflating ListView" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+
+ <!-- Text -->
+ <activity
+ android:name=".EditTextTypeActivity"
+ android:label="Text/EditText Typing" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".TextCacheLowHitrateActivity"
+ android:label="Text/Layout Cache Low Hitrate" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".TextCacheHighHitrateActivity"
+ android:label="Text/Layout Cache High Hitrate" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.uibench.TEST" />
diff --git a/tests/UiBench/build.gradle b/tests/UiBench/build.gradle
index bf0ed11..0756a8a 100644
--- a/tests/UiBench/build.gradle
+++ b/tests/UiBench/build.gradle
@@ -32,6 +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: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_bitmap_upload.xml b/tests/UiBench/res/layout/activity_bitmap_upload.xml
new file mode 100644
index 0000000..70faa07a
--- /dev/null
+++ b/tests/UiBench/res/layout/activity_bitmap_upload.xml
@@ -0,0 +1,52 @@
+<?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:id="@+id/upload_root"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="10dp"
+ android:clipToPadding="false">
+ <android.support.v7.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+ <view class="com.android.test.uibench.BitmapUploadActivity$UploadView"
+ android:id="@+id/upload_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ </android.support.v7.widget.CardView>
+
+ <android.support.v4.widget.Space
+ android:layout_height="10dp"
+ android:layout_width="match_parent" />
+
+ <android.support.v7.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <android.support.v4.widget.Space
+ android:layout_height="10dp"
+ android:layout_width="match_parent" />
+
+ <android.support.v7.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/UiBench/res/layout/activity_invalidate.xml b/tests/UiBench/res/layout/activity_invalidate.xml
new file mode 100644
index 0000000..34bcca9
--- /dev/null
+++ b/tests/UiBench/res/layout/activity_invalidate.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:id="@+id/invalidate_root"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+ <include layout="@layout/invalidate_row"/>
+</LinearLayout>
\ No newline at end of file
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/card_row.xml b/tests/UiBench/res/layout/card_row.xml
new file mode 100644
index 0000000..215f9df
--- /dev/null
+++ b/tests/UiBench/res/layout/card_row.xml
@@ -0,0 +1,45 @@
+<?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:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="100dp"
+ android:paddingStart="10dp"
+ android:paddingEnd="10dp"
+ android:paddingTop="5dp"
+ android:paddingBottom="5dp"
+ android:clipToPadding="false"
+ android:background="@null">
+ <android.support.v7.widget.CardView
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1">
+ <TextView
+ android:id="@+id/card_text"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ </android.support.v7.widget.CardView>
+
+ <android.support.v4.widget.Space
+ android:layout_height="match_parent"
+ android:layout_width="10dp" />
+
+ <android.support.v7.widget.CardView
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/UiBench/res/layout/invalidate_row.xml b/tests/UiBench/res/layout/invalidate_row.xml
new file mode 100644
index 0000000..9feefde
--- /dev/null
+++ b/tests/UiBench/res/layout/invalidate_row.xml
@@ -0,0 +1,84 @@
+<?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:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ <view class="com.android.test.uibench.InvalidateActivity$ColorView"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+</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/BitmapUploadActivity.java b/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java
new file mode 100644
index 0000000..e2bf897
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java
@@ -0,0 +1,96 @@
+/*
+ * 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.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.View;
+
+public class BitmapUploadActivity extends AppCompatActivity {
+ public static class UploadView extends View {
+ private int mColorValue;
+ private Bitmap mBitmap;
+ private final DisplayMetrics mMetrics = new DisplayMetrics();
+ private final Rect mRect = new Rect();
+
+ public UploadView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @SuppressWarnings("unused")
+ public void setColorValue(int colorValue) {
+ if (colorValue == mColorValue) return;
+
+ mColorValue = colorValue;
+
+ // modify the bitmap's color to ensure it's uploaded to the GPU
+ mBitmap.eraseColor(Color.rgb(mColorValue, 255 - mColorValue, 255));
+
+ invalidate();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ getDisplay().getMetrics(mMetrics);
+ int minDisplayDimen = Math.min(mMetrics.widthPixels, mMetrics.heightPixels);
+ int bitmapSize = Math.min((int) (minDisplayDimen * 0.75), 720);
+ if (mBitmap == null
+ || mBitmap.getWidth() != bitmapSize
+ || mBitmap.getHeight() != bitmapSize) {
+ mBitmap = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mBitmap != null) {
+ mRect.set(0, 0, getWidth(), getHeight());
+ canvas.drawBitmap(mBitmap, null, mRect, null);
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_bitmap_upload);
+
+ // animate color to force bitmap uploads
+ UploadView uploadView = (UploadView) findViewById(R.id.upload_view);
+ ObjectAnimator colorValueAnimator = ObjectAnimator.ofInt(uploadView, "colorValue", 0, 255);
+ colorValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ colorValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ colorValueAnimator.start();
+
+ // animate scene root to guarantee there's a minimum amount of GPU rendering work
+ View uploadRoot = findViewById(R.id.upload_root);
+ ObjectAnimator yAnimator = ObjectAnimator.ofFloat(uploadRoot, "translationY", 0, 100);
+ yAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ yAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ yAnimator.start();
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/DialogListActivity.java b/tests/UiBench/src/com/android/test/uibench/DialogListActivity.java
new file mode 100644
index 0000000..fe712d5
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/DialogListActivity.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 com.android.test.uibench;
+
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+public class DialogListActivity extends AppCompatActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ListView listView = new ListView(this);
+ listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
+ TextUtils.buildSimpleStringList()));
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Dialog");
+ builder.setView(listView);
+ builder.create().show();
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/EditTextTypeActivity.java b/tests/UiBench/src/com/android/test/uibench/EditTextTypeActivity.java
new file mode 100644
index 0000000..08ab510
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/EditTextTypeActivity.java
@@ -0,0 +1,110 @@
+/*
+ * 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.Instrumentation;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.support.v7.app.AppCompatActivity;
+import android.view.KeyEvent;
+import android.widget.EditText;
+
+import java.util.concurrent.Semaphore;
+
+/**
+ * Note: currently incomplete, complexity of input continuously grows, instead of looping
+ * over a stable amount of work.
+ *
+ * Simulates typing continuously into an EditText.
+ */
+public class EditTextTypeActivity extends AppCompatActivity {
+ Thread mThread;
+
+ private static String sSeedText = "";
+ static {
+ final int count = 100;
+ final String string = "hello ";
+
+ StringBuilder builder = new StringBuilder(count * string.length());
+ for (int i = 0; i < count; i++) {
+ builder.append(string);
+ }
+ sSeedText = builder.toString();
+ }
+
+ final Object mLock = new Object();
+ boolean mShouldStop = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ EditText editText = new EditText(this);
+ editText.setText(sSeedText);
+ setContentView(editText);
+
+ final Instrumentation instrumentation = new Instrumentation();
+ final Semaphore sem = new Semaphore(0);
+ MessageQueue.IdleHandler handler = new MessageQueue.IdleHandler() {
+ @Override
+ public boolean queueIdle() {
+ // TODO: consider other signaling approaches
+ sem.release();
+ return true;
+ }
+ };
+ Looper.myQueue().addIdleHandler(handler);
+ synchronized (mLock) {
+ mShouldStop = false;
+ }
+ mThread = new Thread(new Runnable() {
+ int codes[] = { KeyEvent.KEYCODE_H, KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_L,
+ KeyEvent.KEYCODE_L, KeyEvent.KEYCODE_O, KeyEvent.KEYCODE_SPACE };
+ int i = 0;
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ sem.acquire();
+ } catch (InterruptedException e) {
+ // TODO, maybe
+ }
+ int code = codes[i % codes.length];
+ if (i % 100 == 99) code = KeyEvent.KEYCODE_ENTER;
+
+ synchronized (mLock) {
+ if (mShouldStop) break;
+ }
+
+ // TODO: bit of a race here, since the event can arrive after pause/stop.
+ // (Can't synchronize on key send, since it's synchronous.)
+ instrumentation.sendKeyDownUpSync(code);
+ i++;
+ }
+ }
+ });
+ mThread.start();
+ }
+
+ @Override
+ protected void onPause() {
+ synchronized (mLock) {
+ mShouldStop = true;
+ }
+ super.onPause();
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java b/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java
index ce79259..a12742d 100644
--- a/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java
@@ -18,39 +18,19 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.SurfaceTexture;
-import android.opengl.GLUtils;
import android.os.Bundle;
-import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.TextureView;
-import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-import javax.microedition.khronos.egl.EGLSurface;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-
-import static android.opengl.GLES20.*;
+import com.android.test.uibench.opengl.ImageFlipRenderThread;
public class GlTextureViewActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {
- private RenderThread mRenderThread;
+ private ImageFlipRenderThread mRenderThread;
private TextureView mTextureView;
@Override
@@ -66,7 +46,7 @@
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- mRenderThread = new RenderThread(getResources(), surface);
+ mRenderThread = new ImageFlipRenderThread(getResources(), surface);
mRenderThread.start();
mTextureView.setCameraDistance(5000);
@@ -94,7 +74,7 @@
try {
mRenderThread.join();
} catch (InterruptedException e) {
- Log.e(RenderThread.LOG_TAG, "Could not wait for render thread");
+ Log.e(ImageFlipRenderThread.LOG_TAG, "Could not wait for render thread");
}
return true;
}
@@ -103,311 +83,4 @@
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
- private static class RenderThread extends Thread {
- private static final String LOG_TAG = "GLTextureView";
-
- static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
- static final int EGL_OPENGL_ES2_BIT = 4;
-
- private volatile boolean mFinished;
-
- private final Resources mResources;
- private final SurfaceTexture mSurface;
-
- private EGL10 mEgl;
- private EGLDisplay mEglDisplay;
- private EGLConfig mEglConfig;
- private EGLContext mEglContext;
- private EGLSurface mEglSurface;
-
- RenderThread(Resources resources, SurfaceTexture surface) {
- mResources = resources;
- mSurface = surface;
- }
-
- private static final String sSimpleVS =
- "attribute vec4 position;\n" +
- "attribute vec2 texCoords;\n" +
- "varying vec2 outTexCoords;\n" +
- "\nvoid main(void) {\n" +
- " outTexCoords = texCoords;\n" +
- " gl_Position = position;\n" +
- "}\n\n";
- private static final String sSimpleFS =
- "precision mediump float;\n\n" +
- "varying vec2 outTexCoords;\n" +
- "uniform sampler2D texture;\n" +
- "\nvoid main(void) {\n" +
- " gl_FragColor = texture2D(texture, outTexCoords);\n" +
- "}\n\n";
-
- private static final int FLOAT_SIZE_BYTES = 4;
- private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
- private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
- private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
- private final float[] mTriangleVerticesData = {
- // X, Y, Z, U, V
- -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
- 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
- -1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
- 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
- };
-
- @Override
- public void run() {
- initGL();
-
- FloatBuffer triangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length
- * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
- triangleVertices.put(mTriangleVerticesData).position(0);
-
- int texture = loadTexture(R.drawable.large_photo);
- int program = buildProgram(sSimpleVS, sSimpleFS);
-
- int attribPosition = glGetAttribLocation(program, "position");
- checkGlError();
-
- int attribTexCoords = glGetAttribLocation(program, "texCoords");
- checkGlError();
-
- int uniformTexture = glGetUniformLocation(program, "texture");
- checkGlError();
-
- glBindTexture(GL_TEXTURE_2D, texture);
- checkGlError();
-
- glUseProgram(program);
- checkGlError();
-
- glEnableVertexAttribArray(attribPosition);
- checkGlError();
-
- glEnableVertexAttribArray(attribTexCoords);
- checkGlError();
-
- glUniform1i(uniformTexture, 0);
- checkGlError();
-
- // drawQuad
- triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
- glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
- TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
- checkGlError();
-
- triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
- glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
- TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
- checkGlError();
-
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- checkGlError();
-
- while (!mFinished) {
- checkCurrent();
-
- glClear(GL_COLOR_BUFFER_BIT);
- checkGlError();
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- checkGlError();
-
- if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
- throw new RuntimeException("Cannot swap buffers");
- }
- checkEglError();
-
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- // Ignore
- }
- }
-
- finishGL();
- }
-
- private int loadTexture(int resource) {
- int[] textures = new int[1];
-
- glActiveTexture(GL_TEXTURE0);
- glGenTextures(1, textures, 0);
- checkGlError();
-
- int texture = textures[0];
- glBindTexture(GL_TEXTURE_2D, texture);
- checkGlError();
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- Bitmap bitmap = BitmapFactory.decodeResource(mResources, resource);
-
- GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
- checkGlError();
-
- bitmap.recycle();
-
- return texture;
- }
-
- private static int buildProgram(String vertex, String fragment) {
- int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
- if (vertexShader == 0) return 0;
-
- int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
- if (fragmentShader == 0) return 0;
-
- int program = glCreateProgram();
- glAttachShader(program, vertexShader);
- checkGlError();
-
- glAttachShader(program, fragmentShader);
- checkGlError();
-
- glLinkProgram(program);
- checkGlError();
-
- int[] status = new int[1];
- glGetProgramiv(program, GL_LINK_STATUS, status, 0);
- if (status[0] != GL_TRUE) {
- String error = glGetProgramInfoLog(program);
- Log.d(LOG_TAG, "Error while linking program:\n" + error);
- glDeleteShader(vertexShader);
- glDeleteShader(fragmentShader);
- glDeleteProgram(program);
- return 0;
- }
-
- return program;
- }
-
- private static int buildShader(String source, int type) {
- int shader = glCreateShader(type);
-
- glShaderSource(shader, source);
- checkGlError();
-
- glCompileShader(shader);
- checkGlError();
-
- int[] status = new int[1];
- glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
- if (status[0] != GL_TRUE) {
- String error = glGetShaderInfoLog(shader);
- Log.d(LOG_TAG, "Error while compiling shader:\n" + error);
- glDeleteShader(shader);
- return 0;
- }
-
- return shader;
- }
-
- private void checkEglError() {
- int error = mEgl.eglGetError();
- if (error != EGL10.EGL_SUCCESS) {
- Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error));
- }
- }
-
- private static void checkGlError() {
- int error = glGetError();
- if (error != GL_NO_ERROR) {
- Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
- }
- }
-
- private void finishGL() {
- mEgl.eglDestroyContext(mEglDisplay, mEglContext);
- mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
- }
-
- private void checkCurrent() {
- if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
- !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
- if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- throw new RuntimeException("eglMakeCurrent failed "
- + GLUtils.getEGLErrorString(mEgl.eglGetError()));
- }
- }
- }
-
- private void initGL() {
- mEgl = (EGL10) EGLContext.getEGL();
-
- mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
- if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
- throw new RuntimeException("eglGetDisplay failed "
- + GLUtils.getEGLErrorString(mEgl.eglGetError()));
- }
-
- int[] version = new int[2];
- if (!mEgl.eglInitialize(mEglDisplay, version)) {
- throw new RuntimeException("eglInitialize failed " +
- GLUtils.getEGLErrorString(mEgl.eglGetError()));
- }
-
- mEglConfig = chooseEglConfig();
- if (mEglConfig == null) {
- throw new RuntimeException("eglConfig not initialized");
- }
-
- mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
-
- mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
-
- if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
- int error = mEgl.eglGetError();
- if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
- Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
- return;
- }
- throw new RuntimeException("createWindowSurface failed "
- + GLUtils.getEGLErrorString(error));
- }
-
- if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- throw new RuntimeException("eglMakeCurrent failed "
- + GLUtils.getEGLErrorString(mEgl.eglGetError()));
- }
- }
-
-
- EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
- int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
- return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
- }
-
- private EGLConfig chooseEglConfig() {
- int[] configsCount = new int[1];
- EGLConfig[] configs = new EGLConfig[1];
- int[] configSpec = getConfig();
- if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
- throw new IllegalArgumentException("eglChooseConfig failed " +
- GLUtils.getEGLErrorString(mEgl.eglGetError()));
- } else if (configsCount[0] > 0) {
- return configs[0];
- }
- return null;
- }
-
- private static int[] getConfig() {
- return new int[] {
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL10.EGL_RED_SIZE, 8,
- EGL10.EGL_GREEN_SIZE, 8,
- EGL10.EGL_BLUE_SIZE, 8,
- EGL10.EGL_ALPHA_SIZE, 8,
- EGL10.EGL_DEPTH_SIZE, 0,
- EGL10.EGL_STENCIL_SIZE, 0,
- EGL10.EGL_NONE
- };
- }
-
- void finish() {
- mFinished = true;
- }
- }
}
\ No newline at end of file
diff --git a/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java b/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java
new file mode 100644
index 0000000..603244e
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.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.test.uibench;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+
+import com.android.test.uibench.listview.CompatListActivity;
+
+public class InflatingListActivity extends CompatListActivity {
+ @Override
+ protected ListAdapter createListAdapter() {
+ return new ArrayAdapter<String>(this,
+ android.R.layout.simple_list_item_1, TextUtils.buildSimpleStringList()) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // pathological getView behavior: drop convertView on the floor to force inflation
+ return super.getView(position, null, parent);
+ }
+ };
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java b/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java
new file mode 100644
index 0000000..93d67a6
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java
@@ -0,0 +1,88 @@
+/*
+ * 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.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.annotation.ColorInt;
+import android.support.v7.app.AppCompatActivity;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Tests invalidation performance by invalidating a large number of easily rendered views,
+ */
+public class InvalidateActivity extends AppCompatActivity {
+ public static class ColorView extends View {
+ @ColorInt
+ public int mColor;
+
+ public ColorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setColor(@ColorInt int color) {
+ mColor = color;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawColor(mColor);
+ }
+ }
+
+ ColorView[][] mColorViews;
+
+ @SuppressWarnings("unused")
+ public void setColorValue(int colorValue) {
+ @ColorInt int a = Color.rgb(colorValue, 255 - colorValue, 255);
+ @ColorInt int b = Color.rgb(255, colorValue, 255 - colorValue);
+ for (int y = 0; y < mColorViews.length; y++) {
+ for (int x = 0; x < mColorViews[y].length; x++) {
+ mColorViews[y][x].setColor((x + y) % 2 == 0 ? a : b);
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_invalidate);
+
+ ViewGroup root = (ViewGroup) findViewById(R.id.invalidate_root);
+ for (int y = 0; y < root.getChildCount(); y++) {
+ ViewGroup row = (ViewGroup) root.getChildAt(y);
+ if (mColorViews == null) {
+ mColorViews = new ColorView[root.getChildCount()][row.getChildCount()];
+ }
+
+ for (int x = 0; x < row.getChildCount(); x++) {
+ mColorViews[y][x] = (ColorView) row.getChildAt(x);
+ }
+ }
+
+ ObjectAnimator animator = ObjectAnimator.ofInt(this, "colorValue", 0, 255);
+ animator.setRepeatMode(ValueAnimator.REVERSE);
+ animator.setRepeatCount(ValueAnimator.INFINITE);
+ animator.start();
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java b/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java
new file mode 100644
index 0000000..d32f071
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/ShadowGridActivity.java
@@ -0,0 +1,45 @@
+/*
+ * 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.os.Bundle;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.ListFragment;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.ArrayAdapter;
+
+public class ShadowGridActivity extends AppCompatActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FragmentManager fm = getSupportFragmentManager();
+ if (fm.findFragmentById(android.R.id.content) == null) {
+ ListFragment listFragment = new ListFragment() {
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+ }
+ };
+
+ listFragment.setListAdapter(new ArrayAdapter<>(this,
+ R.layout.card_row, R.id.card_text, TextUtils.buildSimpleStringList()));
+ fm.beginTransaction().add(android.R.id.content, listFragment).commit();
+ }
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/TextCacheHighHitrateActivity.java b/tests/UiBench/src/com/android/test/uibench/TextCacheHighHitrateActivity.java
new file mode 100644
index 0000000..fc5be35
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/TextCacheHighHitrateActivity.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.test.uibench;
+
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+
+import com.android.test.uibench.listview.CompatListActivity;
+
+public class TextCacheHighHitrateActivity extends CompatListActivity {
+ @Override
+ protected ListAdapter createListAdapter() {
+ return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
+ TextUtils.buildParagraphListWithHitPercentage(80));
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/TextCacheLowHitrateActivity.java b/tests/UiBench/src/com/android/test/uibench/TextCacheLowHitrateActivity.java
new file mode 100644
index 0000000..14dc437
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/TextCacheLowHitrateActivity.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.test.uibench;
+
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+
+import com.android.test.uibench.listview.CompatListActivity;
+
+public class TextCacheLowHitrateActivity extends CompatListActivity {
+ @Override
+ protected ListAdapter createListAdapter() {
+ return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
+ TextUtils.buildParagraphListWithHitPercentage(20));
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/TextUtils.java b/tests/UiBench/src/com/android/test/uibench/TextUtils.java
new file mode 100644
index 0000000..d88ca1e
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/TextUtils.java
@@ -0,0 +1,91 @@
+/*
+ * 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 java.util.Random;
+
+public class TextUtils {
+ private static final int STRING_COUNT = 200;
+ private static final int SIMPLE_STRING_LENGTH = 10;
+
+ /**
+ * Create word of random assortment of lower/upper case letters
+ */
+ private static String randomWord(Random random, int length) {
+ String result = "";
+ for (int j = 0; j < length; j++) {
+ // add random letter
+ int base = random.nextInt(2) == 0 ? 'A' : 'a';
+ result += (char)(random.nextInt(26) + base);
+ }
+ return result;
+ }
+
+ public static String[] buildSimpleStringList() {
+ String[] strings = new String[STRING_COUNT];
+ Random random = new Random(0);
+ for (int i = 0; i < strings.length; i++) {
+ strings[i] = randomWord(random, SIMPLE_STRING_LENGTH);
+ }
+ return strings;
+ }
+
+ // a small number of strings reused frequently, expected to hit
+ // in the word-granularity text layout cache
+ static final String[] CACHE_HIT_STRINGS = new String[] {
+ "a",
+ "small",
+ "number",
+ "of",
+ "strings",
+ "reused",
+ "frequently"
+ };
+
+ private static final int WORDS_IN_PARAGRAPH = 150;
+
+ // misses are fairly long 'words' to ensure they miss
+ private static final int PARAGRAPH_MISS_MIN_LENGTH = 4;
+ private static final int PARAGRAPH_MISS_MAX_LENGTH = 9;
+
+ static String[] buildParagraphListWithHitPercentage(int hitPercentage) {
+ if (hitPercentage < 0 || hitPercentage > 100) throw new IllegalArgumentException();
+
+ String[] strings = new String[STRING_COUNT];
+ Random random = new Random(0);
+ for (int i = 0; i < strings.length; i++) {
+ String result = "";
+ for (int word = 0; word < WORDS_IN_PARAGRAPH; word++) {
+ if (word != 0) {
+ result += " ";
+ }
+ if (random.nextInt(100) < hitPercentage) {
+ // add a common word, which is very likely to hit in the cache
+ result += CACHE_HIT_STRINGS[random.nextInt(CACHE_HIT_STRINGS.length)];
+ } else {
+ // construct a random word, which will *most likely* miss
+ int length = PARAGRAPH_MISS_MIN_LENGTH;
+ length += random.nextInt(PARAGRAPH_MISS_MAX_LENGTH - PARAGRAPH_MISS_MIN_LENGTH);
+
+ result += randomWord(random, length);
+ }
+ }
+ strings[i] = result;
+ }
+
+ return strings;
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java b/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java
index 0af3471..2625a99 100644
--- a/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java
@@ -15,41 +15,15 @@
*/
package com.android.test.uibench;
-import android.os.Bundle;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.ListFragment;
-import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
-import java.util.Random;
+import com.android.test.uibench.listview.CompatListActivity;
-public class TrivialListActivity extends AppCompatActivity {
- static final int STRING_LENGTH = 10;
-
- static String[] buildStringList() {
- String[] strings = new String[200];
- Random random = new Random(0);
- for (int i = 0; i < strings.length; i++) {
- String result = "";
- for (int j = 0; j < STRING_LENGTH; j++) {
- // add random letter
- result += (char)(random.nextInt(26) + 65);
- }
- strings[i] = result;
- }
- return strings;
- }
-
+public class TrivialListActivity extends CompatListActivity {
@Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- FragmentManager fm = getSupportFragmentManager();
- if (fm.findFragmentById(android.R.id.content) == null) {
- ListFragment listFragment = new ListFragment();
- listFragment.setListAdapter(new ArrayAdapter<>(TrivialListActivity.this,
- android.R.layout.simple_list_item_1, buildStringList()));
- fm.beginTransaction().add(android.R.id.content, listFragment).commit();
- }
+ protected ListAdapter createListAdapter() {
+ return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
+ TextUtils.buildSimpleStringList());
}
}
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/listview/CompatListActivity.java b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
new file mode 100644
index 0000000..214c074
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
@@ -0,0 +1,39 @@
+/*
+ * 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.listview;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.ListFragment;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.ListAdapter;
+
+public abstract class CompatListActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FragmentManager fm = getSupportFragmentManager();
+ if (fm.findFragmentById(android.R.id.content) == null) {
+ ListFragment listFragment = new ListFragment();
+ listFragment.setListAdapter(createListAdapter());
+ fm.beginTransaction().add(android.R.id.content, listFragment).commit();
+ }
+ }
+
+ protected abstract ListAdapter createListAdapter();
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/opengl/ImageFlipRenderThread.java b/tests/UiBench/src/com/android/test/uibench/opengl/ImageFlipRenderThread.java
new file mode 100644
index 0000000..119ce52
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/opengl/ImageFlipRenderThread.java
@@ -0,0 +1,389 @@
+/*
+ * 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.opengl;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.SurfaceTexture;
+import android.opengl.GLUtils;
+import android.util.Log;
+
+import com.android.test.uibench.R;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+
+import static android.opengl.GLES20.GL_CLAMP_TO_EDGE;
+import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
+import static android.opengl.GLES20.GL_COMPILE_STATUS;
+import static android.opengl.GLES20.GL_FLOAT;
+import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
+import static android.opengl.GLES20.GL_LINEAR;
+import static android.opengl.GLES20.GL_LINK_STATUS;
+import static android.opengl.GLES20.GL_NO_ERROR;
+import static android.opengl.GLES20.GL_RGBA;
+import static android.opengl.GLES20.GL_TEXTURE0;
+import static android.opengl.GLES20.GL_TEXTURE_2D;
+import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
+import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
+import static android.opengl.GLES20.GL_TEXTURE_WRAP_S;
+import static android.opengl.GLES20.GL_TEXTURE_WRAP_T;
+import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
+import static android.opengl.GLES20.GL_TRUE;
+import static android.opengl.GLES20.GL_UNSIGNED_BYTE;
+import static android.opengl.GLES20.GL_VERTEX_SHADER;
+import static android.opengl.GLES20.glActiveTexture;
+import static android.opengl.GLES20.glAttachShader;
+import static android.opengl.GLES20.glBindTexture;
+import static android.opengl.GLES20.glClear;
+import static android.opengl.GLES20.glClearColor;
+import static android.opengl.GLES20.glCompileShader;
+import static android.opengl.GLES20.glCreateProgram;
+import static android.opengl.GLES20.glCreateShader;
+import static android.opengl.GLES20.glDeleteProgram;
+import static android.opengl.GLES20.glDeleteShader;
+import static android.opengl.GLES20.glDrawArrays;
+import static android.opengl.GLES20.glEnableVertexAttribArray;
+import static android.opengl.GLES20.glGenTextures;
+import static android.opengl.GLES20.glGetAttribLocation;
+import static android.opengl.GLES20.glGetError;
+import static android.opengl.GLES20.glGetProgramInfoLog;
+import static android.opengl.GLES20.glGetProgramiv;
+import static android.opengl.GLES20.glGetShaderInfoLog;
+import static android.opengl.GLES20.glGetShaderiv;
+import static android.opengl.GLES20.glGetUniformLocation;
+import static android.opengl.GLES20.glLinkProgram;
+import static android.opengl.GLES20.glShaderSource;
+import static android.opengl.GLES20.glTexParameteri;
+import static android.opengl.GLES20.glUniform1i;
+import static android.opengl.GLES20.glUseProgram;
+import static android.opengl.GLES20.glVertexAttribPointer;
+
+public class ImageFlipRenderThread extends Thread {
+ public static final String LOG_TAG = "GLTextureView";
+
+ static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ static final int EGL_OPENGL_ES2_BIT = 4;
+
+ private volatile boolean mFinished;
+
+ private final Resources mResources;
+ private final SurfaceTexture mSurface;
+
+ private EGL10 mEgl;
+ private EGLDisplay mEglDisplay;
+ private EGLConfig mEglConfig;
+ private EGLContext mEglContext;
+ private EGLSurface mEglSurface;
+
+ public ImageFlipRenderThread(Resources resources, SurfaceTexture surface) {
+ mResources = resources;
+ mSurface = surface;
+ }
+
+ private static final String sSimpleVS =
+ "attribute vec4 position;\n" +
+ "attribute vec2 texCoords;\n" +
+ "varying vec2 outTexCoords;\n" +
+ "\nvoid main(void) {\n" +
+ " outTexCoords = texCoords;\n" +
+ " gl_Position = position;\n" +
+ "}\n\n";
+ private static final String sSimpleFS =
+ "precision mediump float;\n\n" +
+ "varying vec2 outTexCoords;\n" +
+ "uniform sampler2D texture;\n" +
+ "\nvoid main(void) {\n" +
+ " gl_FragColor = texture2D(texture, outTexCoords);\n" +
+ "}\n\n";
+
+ private static final int FLOAT_SIZE_BYTES = 4;
+ private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
+ private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
+ private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
+ private final float[] mTriangleVerticesData = {
+ // X, Y, Z, U, V
+ -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
+ -1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
+ 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
+ };
+
+ @Override
+ public void run() {
+ initGL();
+
+ FloatBuffer triangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length
+ * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
+ triangleVertices.put(mTriangleVerticesData).position(0);
+
+ int texture = loadTexture(R.drawable.large_photo);
+ int program = buildProgram(sSimpleVS, sSimpleFS);
+
+ int attribPosition = glGetAttribLocation(program, "position");
+ checkGlError();
+
+ int attribTexCoords = glGetAttribLocation(program, "texCoords");
+ checkGlError();
+
+ int uniformTexture = glGetUniformLocation(program, "texture");
+ checkGlError();
+
+ glBindTexture(GL_TEXTURE_2D, texture);
+ checkGlError();
+
+ glUseProgram(program);
+ checkGlError();
+
+ glEnableVertexAttribArray(attribPosition);
+ checkGlError();
+
+ glEnableVertexAttribArray(attribTexCoords);
+ checkGlError();
+
+ glUniform1i(uniformTexture, 0);
+ checkGlError();
+
+ // drawQuad
+ triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+ glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
+ checkGlError();
+
+ triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+ glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
+ checkGlError();
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ checkGlError();
+
+ while (!mFinished) {
+ checkCurrent();
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ checkGlError();
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ checkGlError();
+
+ if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+ throw new RuntimeException("Cannot swap buffers");
+ }
+ checkEglError();
+
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+
+ finishGL();
+ }
+
+ private int loadTexture(int resource) {
+ int[] textures = new int[1];
+
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(1, textures, 0);
+ checkGlError();
+
+ int texture = textures[0];
+ glBindTexture(GL_TEXTURE_2D, texture);
+ checkGlError();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ Bitmap bitmap = BitmapFactory.decodeResource(mResources, resource);
+
+ GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
+ checkGlError();
+
+ bitmap.recycle();
+
+ return texture;
+ }
+
+ private static int buildProgram(String vertex, String fragment) {
+ int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+ if (vertexShader == 0) return 0;
+
+ int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+ if (fragmentShader == 0) return 0;
+
+ int program = glCreateProgram();
+ glAttachShader(program, vertexShader);
+ checkGlError();
+
+ glAttachShader(program, fragmentShader);
+ checkGlError();
+
+ glLinkProgram(program);
+ checkGlError();
+
+ int[] status = new int[1];
+ glGetProgramiv(program, GL_LINK_STATUS, status, 0);
+ if (status[0] != GL_TRUE) {
+ String error = glGetProgramInfoLog(program);
+ Log.d(LOG_TAG, "Error while linking program:\n" + error);
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ glDeleteProgram(program);
+ return 0;
+ }
+
+ return program;
+ }
+
+ private static int buildShader(String source, int type) {
+ int shader = glCreateShader(type);
+
+ glShaderSource(shader, source);
+ checkGlError();
+
+ glCompileShader(shader);
+ checkGlError();
+
+ int[] status = new int[1];
+ glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
+ if (status[0] != GL_TRUE) {
+ String error = glGetShaderInfoLog(shader);
+ Log.d(LOG_TAG, "Error while compiling shader:\n" + error);
+ glDeleteShader(shader);
+ return 0;
+ }
+
+ return shader;
+ }
+
+ private void checkEglError() {
+ int error = mEgl.eglGetError();
+ if (error != EGL10.EGL_SUCCESS) {
+ Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error));
+ }
+ }
+
+ private static void checkGlError() {
+ int error = glGetError();
+ if (error != GL_NO_ERROR) {
+ Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
+ }
+ }
+
+ private void finishGL() {
+ mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+ mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+ }
+
+ private void checkCurrent() {
+ if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
+ !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throw new RuntimeException("eglMakeCurrent failed "
+ + GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ }
+ }
+ }
+
+ private void initGL() {
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed "
+ + GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ }
+
+ int[] version = new int[2];
+ if (!mEgl.eglInitialize(mEglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ }
+
+ mEglConfig = chooseEglConfig();
+ if (mEglConfig == null) {
+ throw new RuntimeException("eglConfig not initialized");
+ }
+
+ mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
+
+ mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
+
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ int error = mEgl.eglGetError();
+ if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+ Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ return;
+ }
+ throw new RuntimeException("createWindowSurface failed "
+ + GLUtils.getEGLErrorString(error));
+ }
+
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throw new RuntimeException("eglMakeCurrent failed "
+ + GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ }
+ }
+
+
+ EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
+ int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
+ return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
+ }
+
+ private EGLConfig chooseEglConfig() {
+ int[] configsCount = new int[1];
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] configSpec = getConfig();
+ if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
+ throw new IllegalArgumentException("eglChooseConfig failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ } else if (configsCount[0] > 0) {
+ return configs[0];
+ }
+ return null;
+ }
+
+ private static int[] getConfig() {
+ return new int[]{
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_ALPHA_SIZE, 8,
+ EGL10.EGL_DEPTH_SIZE, 0,
+ EGL10.EGL_STENCIL_SIZE, 0,
+ EGL10.EGL_NONE
+ };
+ }
+
+ public void finish() {
+ mFinished = true;
+ }
+}
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/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 6177784..b9e7500 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -32,7 +32,7 @@
*/
public class WindowManagerPermissionTests extends TestCase {
IWindowManager mWm;
-
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -51,7 +51,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.resumeKeyDispatching(null);
fail("IWindowManager.resumeKeyDispatching did not throw SecurityException as"
@@ -61,7 +61,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.setEventDispatching(true);
fail("IWindowManager.setEventDispatching did not throw SecurityException as"
@@ -71,7 +71,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.addWindowToken(null, 0);
fail("IWindowManager.addWindowToken did not throw SecurityException as"
@@ -81,7 +81,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.removeWindowToken(null);
fail("IWindowManager.removeWindowToken did not throw SecurityException as"
@@ -91,9 +91,10 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
- mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null);
+ mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null,
+ Configuration.EMPTY, false);
fail("IWindowManager.addAppToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -101,9 +102,9 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
- mWm.setAppTask(null, 0, null);
+ mWm.setAppTask(null, 0, null, null);
fail("IWindowManager.setAppGroupId did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -111,7 +112,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.updateOrientationFromAppTokens(new Configuration(), null);
fail("IWindowManager.updateOrientationFromAppTokens did not throw SecurityException as"
@@ -121,7 +122,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.setAppOrientation(null, 0);
mWm.addWindowToken(null, 0);
@@ -132,7 +133,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.setFocusedApp(null, false);
fail("IWindowManager.setFocusedApp did not throw SecurityException as"
@@ -142,7 +143,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.prepareAppTransition(0, false);
fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
@@ -152,7 +153,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.executeAppTransition();
fail("IWindowManager.executeAppTransition did not throw SecurityException as"
@@ -162,7 +163,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.setAppStartingWindow(null, "foo", 0, null, null, 0, 0, 0, 0, null, false);
fail("IWindowManager.setAppStartingWindow did not throw SecurityException as"
@@ -172,7 +173,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.setAppWillBeHidden(null);
fail("IWindowManager.setAppWillBeHidden did not throw SecurityException as"
@@ -182,7 +183,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.setAppVisibility(null, false);
fail("IWindowManager.setAppVisibility did not throw SecurityException as"
@@ -192,7 +193,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.startAppFreezingScreen(null, 0);
fail("IWindowManager.startAppFreezingScreen did not throw SecurityException as"
@@ -202,7 +203,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.stopAppFreezingScreen(null, false);
fail("IWindowManager.stopAppFreezingScreen did not throw SecurityException as"
@@ -212,7 +213,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.removeAppToken(null);
fail("IWindowManager.removeAppToken did not throw SecurityException as"
@@ -236,7 +237,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.reenableKeyguard(token);
fail("IWindowManager.reenableKeyguard did not throw SecurityException as"
@@ -246,7 +247,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.exitKeyguardSecurely(null);
fail("IWindowManager.exitKeyguardSecurely did not throw SecurityException as"
@@ -257,7 +258,7 @@
fail("Unexpected remote exception");
}
}
-
+
@SmallTest
public void testSET_ANIMATION_SCALE() {
try {
@@ -269,7 +270,7 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
+
try {
mWm.setAnimationScales(new float[1]);
fail("IWindowManager.setAnimationScales did not throw SecurityException as"
@@ -280,7 +281,7 @@
fail("Unexpected remote exception");
}
}
-
+
@SmallTest
public void testSET_ORIENTATION() {
try {
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 769e2a1..b701445 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -54,7 +54,6 @@
tests/ResourceFilter_test.cpp \
tests/ResourceTable_test.cpp
-aaptHostLdLibs :=
aaptHostStaticLibs := \
libandroidfw \
libpng \
@@ -68,17 +67,13 @@
aaptCFlags := -DAAPT_VERSION=\"$(BUILD_NUMBER_FROM_FILE)\"
aaptCFlags += -Wall -Werror
-ifeq ($(HOST_OS),linux)
- aaptHostLdLibs += -lrt -ldl -lpthread
-endif
+aaptHostLdLibs_linux := -lrt -ldl -lpthread
# Statically link libz for MinGW (Win SDK under Linux),
# and dynamically link for all others.
-ifneq ($(strip $(USE_MINGW)),)
- aaptHostStaticLibs += libz
-else
- aaptHostLdLibs += -lz
-endif
+aaptHostStaticLibs_windows := libz
+aaptHostLdLibs_linux += -lz
+aaptHostLdLibs_darwin := -lz
# ==========================================================
@@ -87,13 +82,13 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libaapt
-LOCAL_CFLAGS += -Wno-format-y2k -DSTATIC_ANDROIDFW_FOR_TOOLS $(aaptCFlags)
-LOCAL_CPPFLAGS += $(aaptCppFlags)
-ifeq (darwin,$(HOST_OS))
-LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
-endif
+LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_CFLAGS := -Wno-format-y2k -DSTATIC_ANDROIDFW_FOR_TOOLS $(aaptCFlags)
+LOCAL_CPPFLAGS := $(aaptCppFlags)
+LOCAL_CFLAGS_darwin := -D_DARWIN_UNLIMITED_STREAMS
LOCAL_SRC_FILES := $(aaptSources)
-LOCAL_STATIC_LIBRARIES += $(aaptHostStaticLibs)
+LOCAL_STATIC_LIBRARIES := $(aaptHostStaticLibs)
+LOCAL_STATIC_LIBRARIES_windows := $(aaptHostStaticLibs_windows)
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -103,11 +98,14 @@
include $(CLEAR_VARS)
LOCAL_MODULE := aapt
-LOCAL_CFLAGS += $(aaptCFlags)
-LOCAL_CPPFLAGS += $(aaptCppFlags)
-LOCAL_LDLIBS += $(aaptHostLdLibs)
+LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_CFLAGS := $(aaptCFlags)
+LOCAL_CPPFLAGS := $(aaptCppFlags)
+LOCAL_LDLIBS_darwin := $(aaptHostLdLibs_darwin)
+LOCAL_LDLIBS_linux := $(aaptHostLdLibs_linux)
LOCAL_SRC_FILES := $(aaptMain)
-LOCAL_STATIC_LIBRARIES += libaapt $(aaptHostStaticLibs)
+LOCAL_STATIC_LIBRARIES := libaapt $(aaptHostStaticLibs)
+LOCAL_STATIC_LIBRARIES_windows := $(aaptHostStaticLibs_windows)
include $(BUILD_HOST_EXECUTABLE)
@@ -116,15 +114,16 @@
# Build the host tests: libaapt_tests
# ==========================================================
include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE := libaapt_tests
-LOCAL_CFLAGS += $(aaptCFlags)
-LOCAL_CPPFLAGS += $(aaptCppFlags)
-LOCAL_LDLIBS += $(aaptHostLdLibs)
-LOCAL_SRC_FILES += $(aaptTests)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)
-LOCAL_STATIC_LIBRARIES += libaapt $(aaptHostStaticLibs)
+LOCAL_CFLAGS := $(aaptCFlags)
+LOCAL_CPPFLAGS := $(aaptCppFlags)
+LOCAL_LDLIBS_darwin := $(aaptHostLdLibs_darwin)
+LOCAL_LDLIBS_linux := $(aaptHostLdLibs_linux)
+LOCAL_SRC_FILES := $(aaptTests)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+LOCAL_STATIC_LIBRARIES := libaapt $(aaptHostStaticLibs)
+LOCAL_STATIC_LIBRARIES_windows := $(aaptHostStaticLibs_windows)
include $(BUILD_HOST_NATIVE_TEST)
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index cbe7c5d..c29bb48 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -66,6 +66,7 @@
mErrorOnMissingConfigEntry(false), mOutputTextSymbols(NULL),
mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL),
mBuildSharedLibrary(false),
+ mBuildAppAsSharedLibrary(false),
mArgc(0), mArgv(NULL)
{}
~Bundle(void) {}
@@ -206,6 +207,8 @@
void setSingleCrunchOutputFile(const char* val) { mSingleCrunchOutputFile = val; }
bool getBuildSharedLibrary() const { return mBuildSharedLibrary; }
void setBuildSharedLibrary(bool val) { mBuildSharedLibrary = val; }
+ bool getBuildAppAsSharedLibrary() const { return mBuildAppAsSharedLibrary; }
+ void setBuildAppAsSharedLibrary(bool val) { mBuildAppAsSharedLibrary = val; }
void setNoVersionVectors(bool val) { mNoVersionVectors = val; }
bool getNoVersionVectors() const { return mNoVersionVectors; }
@@ -327,6 +330,7 @@
const char* mSingleCrunchInputFile;
const char* mSingleCrunchOutputFile;
bool mBuildSharedLibrary;
+ bool mBuildAppAsSharedLibrary;
android::String8 mPlatformVersionCode;
android::String8 mPlatformVersionName;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index d12ab3b..21f47bc2 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -2395,11 +2395,11 @@
// Write the R.java file into the appropriate class directory
// e.g. gen/com/foo/app/R.java
err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
- bundle->getBuildSharedLibrary());
+ bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
} else {
const String8 customPkg(bundle->getCustomPackage());
err = writeResourceSymbols(bundle, assets, customPkg, true,
- bundle->getBuildSharedLibrary());
+ bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
}
if (err < 0) {
goto bail;
@@ -2414,7 +2414,7 @@
while (packageString != NULL) {
// Write the R.java file out with the correct package name
err = writeResourceSymbols(bundle, assets, String8(packageString), true,
- bundle->getBuildSharedLibrary());
+ bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
if (err < 0) {
goto bail;
}
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index bcf0d5e..6411286 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -200,6 +200,9 @@
" --shared-lib\n"
" Make a shared library resource package that can be loaded by an application\n"
" at runtime to access the libraries resources. Implies --non-constant-id.\n"
+ " --app-as-shared-lib\n"
+ " Make an app resource package that also can be loaded as shared library at runtime.\n"
+ " Implies --non-constant-id.\n"
" --error-on-failed-insert\n"
" Forces aapt to return an error if it fails to insert values into the manifest\n"
" with --debug-mode, --min-sdk-version, --target-sdk-version --version-code\n"
@@ -668,6 +671,9 @@
} else if (strcmp(cp, "-shared-lib") == 0) {
bundle.setNonConstantId(true);
bundle.setBuildSharedLibrary(true);
+ } else if (strcmp(cp, "-app-as-shared-lib") == 0) {
+ bundle.setNonConstantId(true);
+ bundle.setBuildAppAsSharedLibrary(true);
} else if (strcmp(cp, "-no-crunch") == 0) {
bundle.setUseCrunchCache(true);
} else if (strcmp(cp, "-ignore-assets") == 0) {
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 ead5e7a..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 efd60a2..0000000
--- a/tools/aidl/Android.mk
+++ /dev/null
@@ -1,29 +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)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- aidl_language_l.l \
- aidl_language_y.y \
- aidl.cpp \
- aidl_language.cpp \
- options.cpp \
- search_path.cpp \
- AST.cpp \
- Type.cpp \
- generate_java.cpp \
- generate_java_binder.cpp \
- generate_java_rpc.cpp
-
-LOCAL_CFLAGS := -g
-LOCAL_MODULE := aidl
-
-include $(BUILD_HOST_EXECUTABLE)
-
-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 2267750..0000000
--- a/tools/aidl/Type.cpp
+++ /dev/null
@@ -1,1442 +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;
-Type* RPC_DATA_TYPE;
-Type* RPC_ERROR_TYPE;
-Type* EVENT_FAKE_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",
- "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",
- "putByte", "getByte", "putByteArray", "createByteArray", "getByteArray");
- NAMES.Add(BYTE_TYPE);
-
- CHAR_TYPE = new CharType();
- NAMES.Add(CHAR_TYPE);
-
- INT_TYPE = new BasicType("int",
- "writeInt", "readInt", "writeIntArray", "createIntArray", "readIntArray",
- "putInteger", "getInteger", "putIntegerArray", "createIntegerArray", "getIntegerArray");
- NAMES.Add(INT_TYPE);
-
- LONG_TYPE = new BasicType("long",
- "writeLong", "readLong", "writeLongArray", "createLongArray", "readLongArray",
- "putLong", "getLong", "putLongArray", "createLongArray", "getLongArray");
- NAMES.Add(LONG_TYPE);
-
- FLOAT_TYPE = new BasicType("float",
- "writeFloat", "readFloat", "writeFloatArray", "createFloatArray", "readFloatArray",
- "putFloat", "getFloat", "putFloatArray", "createFloatArray", "getFloatArray");
- NAMES.Add(FLOAT_TYPE);
-
- DOUBLE_TYPE = new BasicType("double",
- "writeDouble", "readDouble", "writeDoubleArray", "createDoubleArray", "readDoubleArray",
- "putDouble", "getDouble", "putDoubleArray", "createDoubleArray", "getDoubleArray");
- NAMES.Add(DOUBLE_TYPE);
-
- STRING_TYPE = new StringType();
- NAMES.Add(STRING_TYPE);
-
- OBJECT_TYPE = new Type("java.lang", "Object", Type::BUILT_IN, false, 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, 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, false);
- NAMES.Add(CONTEXT_TYPE);
-
- RPC_DATA_TYPE = new RpcDataType();
- NAMES.Add(RPC_DATA_TYPE);
-
- RPC_ERROR_TYPE = new UserDataType("android.support.place.rpc", "RpcError",
- true, __FILE__, __LINE__);
- NAMES.Add(RPC_ERROR_TYPE);
-
- EVENT_FAKE_TYPE = new Type("event", Type::BUILT_IN, false, false, false);
- NAMES.Add(EVENT_FAKE_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 canWriteToRpcData,
- bool canBeOut)
- :m_package(),
- m_name(name),
- m_declFile(""),
- m_declLine(-1),
- m_kind(kind),
- m_canWriteToParcel(canWriteToParcel),
- m_canWriteToRpcData(canWriteToRpcData),
- m_canBeOut(canBeOut)
-{
- m_qualifiedName = name;
-}
-
-Type::Type(const string& package, const string& name,
- int kind, bool canWriteToParcel, bool canWriteToRpcData,
- 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_canWriteToRpcData(canWriteToRpcData),
- 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::RpcCreatorName() 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::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags)
-{
- fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
- __FILE__, __LINE__, m_qualifiedName.c_str());
- addTo->Add(new LiteralExpression("/* WriteToRpcData error "
- + m_qualifiedName + " */"));
-}
-
-void
-Type::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
- Variable** cl)
-{
- fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
- __FILE__, __LINE__, m_qualifiedName.c_str());
- addTo->Add(new LiteralExpression("/* ReadFromRpcData 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,
- const string& marshallRpc, const string& unmarshallRpc,
- const string& writeArrayRpc, const string& createArrayRpc, const string& readArrayRpc)
- :Type(name, BUILT_IN, true, true, false),
- m_marshallParcel(marshallParcel),
- m_unmarshallParcel(unmarshallParcel),
- m_writeArrayParcel(writeArrayParcel),
- m_createArrayParcel(createArrayParcel),
- m_readArrayParcel(readArrayParcel),
- m_marshallRpc(marshallRpc),
- m_unmarshallRpc(unmarshallRpc),
- m_writeArrayRpc(writeArrayRpc),
- m_createArrayRpc(createArrayRpc),
- m_readArrayRpc(readArrayRpc)
-{
-}
-
-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));
-}
-
-void
-BasicType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags)
-{
- addTo->Add(new MethodCall(data, m_marshallRpc, 2, k, v));
-}
-
-void
-BasicType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
- Variable** cl)
-{
- addTo->Add(new Assignment(v, new MethodCall(data, m_unmarshallRpc, 1, k)));
-}
-
-// ================================================================
-
-BooleanType::BooleanType()
- :Type("boolean", BUILT_IN, true, 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));
-}
-
-void
-BooleanType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags)
-{
- addTo->Add(new MethodCall(data, "putBoolean", 2, k, v));
-}
-
-void
-BooleanType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
- Variable** cl)
-{
- addTo->Add(new Assignment(v, new MethodCall(data, "getBoolean", 1, k)));
-}
-
-// ================================================================
-
-CharType::CharType()
- :Type("char", BUILT_IN, true, 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));
-}
-
-void
-CharType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags)
-{
- addTo->Add(new MethodCall(data, "putChar", 2, k, v));
-}
-
-void
-CharType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
- Variable** cl)
-{
- addTo->Add(new Assignment(v, new MethodCall(data, "getChar", 1, k)));
-}
-
-// ================================================================
-
-StringType::StringType()
- :Type("java.lang", "String", BUILT_IN, true, 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));
-}
-
-void
-StringType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags)
-{
- addTo->Add(new MethodCall(data, "putString", 2, k, v));
-}
-
-void
-StringType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, Variable**)
-{
- addTo->Add(new Assignment(v, new MethodCall(data, "getString", 1, k)));
-}
-
-// ================================================================
-
-CharSequenceType::CharSequenceType()
- :Type("java.lang", "CharSequence", BUILT_IN, true, 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, 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, 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, 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, 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, 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, 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, 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, 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, false, 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, 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));
-}
-
-void
-ListType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags)
-{
- addTo->Add(new MethodCall(data, "putList", 2, k, v));
-}
-
-void
-ListType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
- Variable** cl)
-{
- addTo->Add(new Assignment(v, new MethodCall(data, "getList", 1, k)));
-}
-
-// ================================================================
-
-UserDataType::UserDataType(const string& package, const string& name,
- bool builtIn, bool canWriteToParcel, bool canWriteToRpcData,
- const string& declFile, int declLine)
- :Type(package, name, builtIn ? BUILT_IN : USERDATA, canWriteToParcel, canWriteToRpcData,
- true, declFile, declLine)
-{
-}
-
-string
-UserDataType::CreatorName() const
-{
- return QualifiedName() + ".CREATOR";
-}
-
-string
-UserDataType::RpcCreatorName() const
-{
- return QualifiedName() + ".RPC_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)));
-}
-
-void
-UserDataType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags)
-{
- // data.putFlattenable(k, v);
- addTo->Add(new MethodCall(data, "putFlattenable", 2, k, v));
-}
-
-void
-UserDataType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, Variable** cl)
-{
- // data.getFlattenable(k, CLASS.RPC_CREATOR);
- addTo->Add(new Assignment(v, new MethodCall(data, "getFlattenable", 2, k,
- new FieldVariable(v->type, "RPC_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, 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, 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)));
- }
-}
-
-void
-GenericListType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags)
-{
- Type* generic = GenericArgumentTypes()[0];
- if (generic == RPC_DATA_TYPE) {
- addTo->Add(new MethodCall(data, "putRpcDataList", 2, k, v));
- } else if (generic->RpcCreatorName() != "") {
- addTo->Add(new MethodCall(data, "putFlattenableList", 2, k, v));
- } else {
- addTo->Add(new MethodCall(data, "putList", 2, k, v));
- }
-}
-
-void
-GenericListType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, Variable** cl)
-{
- Type* generic = GenericArgumentTypes()[0];
- if (generic == RPC_DATA_TYPE) {
- addTo->Add(new Assignment(v, new MethodCall(data, "getRpcDataList", 2, k)));
- } else if (generic->RpcCreatorName() != "") {
- addTo->Add(new Assignment(v, new MethodCall(data, "getFlattenableList", 2, k,
- new LiteralExpression(generic->RpcCreatorName()))));
- } else {
- string classArg = GenericArgumentTypes()[0]->QualifiedName();
- classArg += ".class";
- addTo->Add(new Assignment(v, new MethodCall(data, "getList", 2, k,
- new LiteralExpression(classArg))));
- }
-}
-
-
-// ================================================================
-
-RpcDataType::RpcDataType()
- :UserDataType("android.support.place.rpc", "RpcData", true, true, true)
-{
-}
-
-void
-RpcDataType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags)
-{
- addTo->Add(new MethodCall(data, "putRpcData", 2, k, v));
-}
-
-void
-RpcDataType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
- Variable** cl)
-{
- addTo->Add(new Assignment(v, new MethodCall(data, "getRpcData", 1, k)));
-}
-
-
-// ================================================================
-
-ClassLoaderType::ClassLoaderType()
- :Type("java.lang", "ClassLoader", BUILT_IN, false, 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 ae12720..0000000
--- a/tools/aidl/Type.h
+++ /dev/null
@@ -1,542 +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 canWriteToRpcData, bool canBeOut);
- Type(const string& package, const string& name,
- int kind, bool canWriteToParcel, bool canWriteToRpcData, 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 CanWriteToRpcData() const { return m_canWriteToRpcData; }
- inline bool CanBeOutParameter() const { return m_canBeOut; }
-
- virtual string ImportType() const;
- virtual string CreatorName() const;
- virtual string RpcCreatorName() 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);
-
- virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags);
- virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, 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_canWriteToRpcData;
- 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,
- const string& marshallRpc,
- const string& unmarshallRpc,
- const string& writeArrayRpc,
- const string& createArrayRpc,
- const string& readArrayRpc);
-
- 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);
-
- virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags);
- virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, Variable** cl);
-
-private:
- string m_marshallParcel;
- string m_unmarshallParcel;
- string m_writeArrayParcel;
- string m_createArrayParcel;
- string m_readArrayParcel;
- string m_marshallRpc;
- string m_unmarshallRpc;
- string m_writeArrayRpc;
- string m_createArrayRpc;
- string m_readArrayRpc;
-};
-
-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);
-
- virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags);
- virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, 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);
-
- virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags);
- virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, 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);
-
- virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags);
- virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, 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);
-
- virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags);
- virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, Variable** cl);
-};
-
-class UserDataType : public Type
-{
-public:
- UserDataType(const string& package, const string& name,
- bool builtIn, bool canWriteToParcel, bool canWriteToRpcData,
- const string& declFile = "", int declLine = -1);
-
- virtual string CreatorName() const;
- virtual string RpcCreatorName() 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);
-
- virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags);
- virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, 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 RpcDataType : public UserDataType
-{
-public:
- RpcDataType();
-
- virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags);
- virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, Variable** cl);
-};
-
-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);
-
- virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, int flags);
- virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data, 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 Type* RPC_DATA_TYPE;
-extern Type* RPC_ERROR_TYPE;
-extern Type* RPC_CONTEXT_TYPE;
-extern Type* EVENT_FAKE_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 438007f..0000000
--- a/tools/aidl/aidl.cpp
+++ /dev/null
@@ -1,1163 +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->flattening_methods & PARCELABLE_DATA) != 0) {
- printf("parcelable %s %s;\n", b->package, b->name.data);
- }
- if ((b->flattening_methods & RPC_DATA) != 0) {
- printf("flattenable %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 ParserCallbacks g_importCallbacks = {
- &main_document_parsed,
- &import_import_parsed
-};
-
-// ==========================================================
-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
- || items->item_type == INTERFACE_TYPE_RPC) {
- 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->flattening_methods & PARCELABLE_DATA) != 0),
- ((p->flattening_methods & RPC_DATA) != 0), filename, p->name.lineno);
- }
- else if (items->item_type == INTERFACE_TYPE_BINDER
- || items->item_type == INTERFACE_TYPE_RPC) {
- 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, 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, false,
- filename, c->name.lineno);
- NAMES.Add(proxy);
- }
- else if (items->item_type == INTERFACE_TYPE_RPC) {
- // for interfaces, also add the service base type, 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 += ".ServiceBase";
- Type* base = new Type(c->package ? c->package : "",
- name, Type::GENERATED, false, false, false,
- filename, c->name.lineno);
- NAMES.Add(base);
- }
- } 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, int kind, 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 == EVENT_FAKE_TYPE) {
- if (kind != INTERFACE_TYPE_RPC) {
- fprintf(stderr, "%s:%d event methods only supported for rpc interfaces\n",
- filename, m->type.type.lineno);
- err = 1;
- }
- } else {
- if (!(kind == INTERFACE_TYPE_BINDER ? returnType->CanWriteToParcel()
- : returnType->CanWriteToRpcData())) {
- 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 == EVENT_FAKE_TYPE) {
- fprintf(stderr, "%s:%d parameter %s (%d) event can not be used as a parameter %s\n",
- filename, m->type.type.lineno, arg->name.data, index,
- arg->type.type.data);
- err = 1;
- goto next;
- }
-
- if (!(kind == INTERFACE_TYPE_BINDER ? t->CanWriteToParcel() : t->CanWriteToRpcData())) {
- 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 (returnType == EVENT_FAKE_TYPE
- && convert_direction(arg->direction.data) != IN_PARAMETER) {
- fprintf(stderr, "%s:%d parameter %d: '%s %s' All paremeters on events must be 'in'.\n",
- filename, m->type.type.lineno, index,
- arg->type.type.data, arg->name.data);
- err = 1;
- goto next;
- }
-
- 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
- || items->item_type == INTERFACE_TYPE_RPC) {
- 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, items->item_type, 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;
- }
- else if (next->item_type == INTERFACE_TYPE_RPC) {
- 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 or flattenables,\n", filename,
- ((user_data_type*)items)->keyword_token.lineno);
- fprintf(stderr, "%s:%d .aidl files that only declare parcelables or flattenables"
- "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 || items->item_type == INTERFACE_TYPE_RPC) {
- 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 || items->item_type == INTERFACE_TYPE_RPC) {
- 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->flattening_methods = PARCELABLE_DATA;
- doc = (document_item_type*)parcl;
- }
- else if (0 == strcmp("flattenable", 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->flattening_methods = RPC_DATA;
- 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;
-}
-
-// ==========================================================
-static 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;
-}
-
-static 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->flattening_methods & PARCELABLE_DATA) != 0) {
- line = "parcelable ";
- }
- if ((parcelable->flattening_methods & RPC_DATA) != 0) {
- line = "flattenable ";
- }
- 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;
-}
-
-// ==========================================================
-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/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 de1370c..0000000
--- a/tools/aidl/aidl_language.h
+++ /dev/null
@@ -1,172 +0,0 @@
-#ifndef DEVICE_TOOLS_AIDL_AIDL_LANGUAGE_H
-#define DEVICE_TOOLS_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,
- INTERFACE_TYPE_RPC
-};
-
-typedef struct document_item_type {
- unsigned item_type;
- struct document_item_type* next;
-} document_item_type;
-
-
-// for user_data_type.flattening_methods
-enum {
- PARCELABLE_DATA = 0x1,
- RPC_DATA = 0x2
-};
-
-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;
- int flattening_methods;
-} 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 // DEVICE_TOOLS_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 3d33e7a..0000000
--- a/tools/aidl/aidl_language_l.l
+++ /dev/null
@@ -1,214 +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; }
-flattenable { SET_BUFFER(FLATTENABLE); return FLATTENABLE; }
-rpc { SET_BUFFER(INTERFACE); return RPC; }
-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 9b40d28..0000000
--- a/tools/aidl/aidl_language_y.y
+++ /dev/null
@@ -1,373 +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 FLATTENABLE
-%token RPC
-%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->flattening_methods = PARCELABLE_DATA;
- $$.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;
- }
- | FLATTENABLE 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->flattening_methods = PARCELABLE_DATA | RPC_DATA;
- $$.user_data = b;
- }
- | FLATTENABLE ';' {
- fprintf(stderr, "%s:%d syntax error in flattenable declaration. Expected type name.\n",
- g_currentFilename, $1.buffer.lineno);
- $$.user_data = NULL;
- }
- | FLATTENABLE error ';' {
- fprintf(stderr, "%s:%d syntax error in flattenable 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;
- }
- | RPC {
- interface_type* c = (interface_type*)malloc(sizeof(interface_type));
- c->document_item.item_type = INTERFACE_TYPE_RPC;
- 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;
- }
- ;
-
-interface_keywords:
- INTERFACE
- | RPC
- ;
-
-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_keywords 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_keywords 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 9e57407..0000000
--- a/tools/aidl/generate_java.cpp
+++ /dev/null
@@ -1,99 +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);
- }
- else if (iface->document_item.item_type == INTERFACE_TYPE_RPC) {
- cl = generate_rpc_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 4bfcfeb..0000000
--- a/tools/aidl/generate_java.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef GENERATE_JAVA_H
-#define 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);
-Class* generate_rpc_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 // 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/generate_java_rpc.cpp b/tools/aidl/generate_java_rpc.cpp
deleted file mode 100644
index 5e4dacc..0000000
--- a/tools/aidl/generate_java_rpc.cpp
+++ /dev/null
@@ -1,1001 +0,0 @@
-#include "generate_java.h"
-#include "Type.h"
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-Type* SERVICE_CONTEXT_TYPE = new Type("android.content",
- "Context", Type::BUILT_IN, false, false, false);
-Type* PRESENTER_BASE_TYPE = new Type("android.support.place.connector",
- "EventListener", Type::BUILT_IN, false, false, false);
-Type* PRESENTER_LISTENER_BASE_TYPE = new Type("android.support.place.connector",
- "EventListener.Listener", Type::BUILT_IN, false, false, false);
-Type* RPC_BROKER_TYPE = new Type("android.support.place.connector", "Broker",
- Type::BUILT_IN, false, false, false);
-Type* RPC_CONTAINER_TYPE = new Type("com.android.athome.connector", "ConnectorContainer",
- Type::BUILT_IN, false, false, false);
-Type* PLACE_INFO_TYPE = new Type("android.support.place.connector", "PlaceInfo",
- Type::BUILT_IN, false, false, false);
-// TODO: Just use Endpoint, so this works for all endpoints.
-Type* RPC_CONNECTOR_TYPE = new Type("android.support.place.connector", "Connector",
- Type::BUILT_IN, false, false, false);
-Type* RPC_ENDPOINT_INFO_TYPE = new UserDataType("android.support.place.rpc",
- "EndpointInfo", true, __FILE__, __LINE__);
-Type* RPC_RESULT_HANDLER_TYPE = new UserDataType("android.support.place.rpc", "RpcResultHandler",
- true, __FILE__, __LINE__);
-Type* RPC_ERROR_LISTENER_TYPE = new Type("android.support.place.rpc", "RpcErrorHandler",
- Type::BUILT_IN, false, false, false);
-Type* RPC_CONTEXT_TYPE = new UserDataType("android.support.place.rpc", "RpcContext", true,
- __FILE__, __LINE__);
-
-static void generate_create_from_data(Type* t, StatementBlock* addTo, const string& key,
- Variable* v, Variable* data, Variable** cl);
-static void generate_new_array(Type* t, StatementBlock* addTo, Variable* v, Variable* from);
-static void generate_write_to_data(Type* t, StatementBlock* addTo, Expression* k, Variable* v,
- Variable* data);
-
-static string
-format_int(int n)
-{
- char str[20];
- sprintf(str, "%d", n);
- return string(str);
-}
-
-static string
-class_name_leaf(const string& str)
-{
- string::size_type pos = str.rfind('.');
- if (pos == string::npos) {
- return str;
- } else {
- return string(str, pos+1);
- }
-}
-
-static string
-results_class_name(const string& n)
-{
- string str = n;
- str[0] = toupper(str[0]);
- str.insert(0, "On");
- return str;
-}
-
-static string
-results_method_name(const string& n)
-{
- string str = n;
- str[0] = toupper(str[0]);
- str.insert(0, "on");
- return str;
-}
-
-static string
-push_method_name(const string& n)
-{
- string str = n;
- str[0] = toupper(str[0]);
- str.insert(0, "push");
- return str;
-}
-
-// =================================================
-class DispatcherClass : public Class
-{
-public:
- DispatcherClass(const interface_type* iface, Expression* target);
- virtual ~DispatcherClass();
-
- void AddMethod(const method_type* method);
- void DoneWithMethods();
-
- Method* processMethod;
- Variable* actionParam;
- Variable* requestParam;
- Variable* rpcContextParam;
- Variable* errorParam;
- Variable* requestData;
- Variable* resultData;
- IfStatement* dispatchIfStatement;
- Expression* targetExpression;
-
-private:
- void generate_process();
-};
-
-DispatcherClass::DispatcherClass(const interface_type* iface, Expression* target)
- :Class(),
- dispatchIfStatement(NULL),
- targetExpression(target)
-{
- generate_process();
-}
-
-DispatcherClass::~DispatcherClass()
-{
-}
-
-void
-DispatcherClass::generate_process()
-{
- // byte[] process(String action, byte[] params, RpcContext context, RpcError status)
- this->processMethod = new Method;
- this->processMethod->modifiers = PUBLIC;
- this->processMethod->returnType = BYTE_TYPE;
- this->processMethod->returnTypeDimension = 1;
- this->processMethod->name = "process";
- this->processMethod->statements = new StatementBlock;
-
- this->actionParam = new Variable(STRING_TYPE, "action");
- this->processMethod->parameters.push_back(this->actionParam);
-
- this->requestParam = new Variable(BYTE_TYPE, "requestParam", 1);
- this->processMethod->parameters.push_back(this->requestParam);
-
- this->rpcContextParam = new Variable(RPC_CONTEXT_TYPE, "context", 0);
- this->processMethod->parameters.push_back(this->rpcContextParam);
-
- this->errorParam = new Variable(RPC_ERROR_TYPE, "errorParam", 0);
- this->processMethod->parameters.push_back(this->errorParam);
-
- this->requestData = new Variable(RPC_DATA_TYPE, "request");
- this->processMethod->statements->Add(new VariableDeclaration(requestData,
- new NewExpression(RPC_DATA_TYPE, 1, this->requestParam)));
-
- this->resultData = new Variable(RPC_DATA_TYPE, "resultData");
- this->processMethod->statements->Add(new VariableDeclaration(this->resultData,
- NULL_VALUE));
-}
-
-void
-DispatcherClass::AddMethod(const method_type* method)
-{
- arg_type* arg;
-
- // The if/switch statement
- IfStatement* ifs = new IfStatement();
- ifs->expression = new MethodCall(new StringLiteralExpression(method->name.data), "equals",
- 1, this->actionParam);
- StatementBlock* block = ifs->statements = new StatementBlock;
- if (this->dispatchIfStatement == NULL) {
- this->dispatchIfStatement = ifs;
- this->processMethod->statements->Add(dispatchIfStatement);
- } else {
- this->dispatchIfStatement->elseif = ifs;
- this->dispatchIfStatement = ifs;
- }
-
- // The call to decl (from above)
- MethodCall* realCall = new MethodCall(this->targetExpression, method->name.data);
-
- // args
- Variable* classLoader = 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;
-
- // Unmarshall the parameter
- block->Add(new VariableDeclaration(v));
- if (convert_direction(arg->direction.data) & IN_PARAMETER) {
- generate_create_from_data(t, block, arg->name.data, v,
- this->requestData, &classLoader);
- } else {
- if (arg->type.dimension == 0) {
- block->Add(new Assignment(v, new NewExpression(v->type)));
- }
- else if (arg->type.dimension == 1) {
- generate_new_array(v->type, block, v, this->requestData);
- }
- else {
- fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__,
- __LINE__);
- }
- }
-
- // Add that parameter to the method call
- realCall->arguments.push_back(v);
-
- arg = arg->next;
- }
-
- // Add a final parameter: RpcContext. Contains data about
- // incoming request (e.g., certificate)
- realCall->arguments.push_back(new Variable(RPC_CONTEXT_TYPE, "context", 0));
-
- Type* returnType = NAMES.Search(method->type.type.data);
- if (returnType == EVENT_FAKE_TYPE) {
- returnType = VOID_TYPE;
- }
-
- // the real call
- bool first = true;
- Variable* _result = NULL;
- if (returnType == VOID_TYPE) {
- block->Add(realCall);
- } else {
- _result = new Variable(returnType, "_result",
- method->type.dimension);
- block->Add(new VariableDeclaration(_result, realCall));
-
- // need the result RpcData
- if (first) {
- block->Add(new Assignment(this->resultData,
- new NewExpression(RPC_DATA_TYPE)));
- first = false;
- }
-
- // marshall the return value
- generate_write_to_data(returnType, block,
- new StringLiteralExpression("_result"), _result, this->resultData);
- }
-
- // out parameters
- int 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) {
- // need the result RpcData
- if (first) {
- block->Add(new Assignment(this->resultData, new NewExpression(RPC_DATA_TYPE)));
- first = false;
- }
-
- generate_write_to_data(t, block, new StringLiteralExpression(arg->name.data),
- v, this->resultData);
- }
-
- arg = arg->next;
- }
-}
-
-void
-DispatcherClass::DoneWithMethods()
-{
- if (this->dispatchIfStatement == NULL) {
- return;
- }
-
- this->elements.push_back(this->processMethod);
-
- IfStatement* fallthrough = new IfStatement();
- fallthrough->statements = new StatementBlock;
- fallthrough->statements->Add(new ReturnStatement(
- new MethodCall(SUPER_VALUE, "process", 4,
- this->actionParam, this->requestParam,
- this->rpcContextParam,
- this->errorParam)));
- this->dispatchIfStatement->elseif = fallthrough;
- IfStatement* s = new IfStatement;
- s->statements = new StatementBlock;
- this->processMethod->statements->Add(s);
- s->expression = new Comparison(this->resultData, "!=", NULL_VALUE);
- s->statements->Add(new ReturnStatement(new MethodCall(this->resultData, "serialize")));
- s->elseif = new IfStatement;
- s = s->elseif;
- s->statements->Add(new ReturnStatement(NULL_VALUE));
-}
-
-// =================================================
-class RpcProxyClass : public Class
-{
-public:
- RpcProxyClass(const interface_type* iface, InterfaceType* interfaceType);
- virtual ~RpcProxyClass();
-
- Variable* endpoint;
- Variable* broker;
-
-private:
- void generate_ctor();
- void generate_get_endpoint_info();
-};
-
-RpcProxyClass::RpcProxyClass(const interface_type* iface, InterfaceType* interfaceType)
- :Class()
-{
- this->comment = gather_comments(iface->comments_token->extra);
- this->modifiers = PUBLIC;
- this->what = Class::CLASS;
- this->type = interfaceType;
-
- // broker
- this->broker = new Variable(RPC_BROKER_TYPE, "_broker");
- this->elements.push_back(new Field(PRIVATE, this->broker));
- // endpoint
- this->endpoint = new Variable(RPC_ENDPOINT_INFO_TYPE, "_endpoint");
- this->elements.push_back(new Field(PRIVATE, this->endpoint));
-
- // methods
- generate_ctor();
- generate_get_endpoint_info();
-}
-
-RpcProxyClass::~RpcProxyClass()
-{
-}
-
-void
-RpcProxyClass::generate_ctor()
-{
- Variable* broker = new Variable(RPC_BROKER_TYPE, "broker");
- Variable* endpoint = new Variable(RPC_ENDPOINT_INFO_TYPE, "endpoint");
- Method* ctor = new Method;
- ctor->modifiers = PUBLIC;
- ctor->name = class_name_leaf(this->type->Name());
- ctor->statements = new StatementBlock;
- ctor->parameters.push_back(broker);
- ctor->parameters.push_back(endpoint);
- this->elements.push_back(ctor);
-
- ctor->statements->Add(new Assignment(this->broker, broker));
- ctor->statements->Add(new Assignment(this->endpoint, endpoint));
-}
-
-void
-RpcProxyClass::generate_get_endpoint_info()
-{
- Method* get = new Method;
- get->modifiers = PUBLIC;
- get->returnType = RPC_ENDPOINT_INFO_TYPE;
- get->name = "getEndpointInfo";
- get->statements = new StatementBlock;
- this->elements.push_back(get);
-
- get->statements->Add(new ReturnStatement(this->endpoint));
-}
-
-// =================================================
-class EventListenerClass : public DispatcherClass
-{
-public:
- EventListenerClass(const interface_type* iface, Type* listenerType);
- virtual ~EventListenerClass();
-
- Variable* _listener;
-
-private:
- void generate_ctor();
-};
-
-Expression*
-generate_get_listener_expression(Type* cast)
-{
- return new Cast(cast, new MethodCall(THIS_VALUE, "getView"));
-}
-
-EventListenerClass::EventListenerClass(const interface_type* iface, Type* listenerType)
- :DispatcherClass(iface, new FieldVariable(THIS_VALUE, "_listener"))
-{
- this->modifiers = PRIVATE;
- this->what = Class::CLASS;
- this->type = new Type(iface->package ? iface->package : "",
- append(iface->name.data, ".Presenter"),
- Type::GENERATED, false, false, false);
- this->extends = PRESENTER_BASE_TYPE;
-
- this->_listener = new Variable(listenerType, "_listener");
- this->elements.push_back(new Field(PRIVATE, this->_listener));
-
- // methods
- generate_ctor();
-}
-
-EventListenerClass::~EventListenerClass()
-{
-}
-
-void
-EventListenerClass::generate_ctor()
-{
- Variable* broker = new Variable(RPC_BROKER_TYPE, "broker");
- Variable* listener = new Variable(this->_listener->type, "listener");
- Method* ctor = new Method;
- ctor->modifiers = PUBLIC;
- ctor->name = class_name_leaf(this->type->Name());
- ctor->statements = new StatementBlock;
- ctor->parameters.push_back(broker);
- ctor->parameters.push_back(listener);
- this->elements.push_back(ctor);
-
- ctor->statements->Add(new MethodCall("super", 2, broker, listener));
- ctor->statements->Add(new Assignment(this->_listener, listener));
-}
-
-// =================================================
-class ListenerClass : public Class
-{
-public:
- ListenerClass(const interface_type* iface);
- virtual ~ListenerClass();
-
- bool needed;
-
-private:
- void generate_ctor();
-};
-
-ListenerClass::ListenerClass(const interface_type* iface)
- :Class(),
- needed(false)
-{
- this->comment = "/** Extend this to listen to the events from this class. */";
- this->modifiers = STATIC | PUBLIC ;
- this->what = Class::CLASS;
- this->type = new Type(iface->package ? iface->package : "",
- append(iface->name.data, ".Listener"),
- Type::GENERATED, false, false, false);
- this->extends = PRESENTER_LISTENER_BASE_TYPE;
-}
-
-ListenerClass::~ListenerClass()
-{
-}
-
-// =================================================
-class EndpointBaseClass : public DispatcherClass
-{
-public:
- EndpointBaseClass(const interface_type* iface);
- virtual ~EndpointBaseClass();
-
- bool needed;
-
-private:
- void generate_ctor();
-};
-
-EndpointBaseClass::EndpointBaseClass(const interface_type* iface)
- :DispatcherClass(iface, THIS_VALUE),
- needed(false)
-{
- this->comment = "/** Extend this to implement a link service. */";
- this->modifiers = STATIC | PUBLIC | ABSTRACT;
- this->what = Class::CLASS;
- this->type = new Type(iface->package ? iface->package : "",
- append(iface->name.data, ".EndpointBase"),
- Type::GENERATED, false, false, false);
- this->extends = RPC_CONNECTOR_TYPE;
-
- // methods
- generate_ctor();
-}
-
-EndpointBaseClass::~EndpointBaseClass()
-{
-}
-
-void
-EndpointBaseClass::generate_ctor()
-{
- Variable* container = new Variable(RPC_CONTAINER_TYPE, "container");
- Variable* broker = new Variable(RPC_BROKER_TYPE, "broker");
- Variable* place = new Variable(PLACE_INFO_TYPE, "placeInfo");
- Method* ctor = new Method;
- ctor->modifiers = PUBLIC;
- ctor->name = class_name_leaf(this->type->Name());
- ctor->statements = new StatementBlock;
- ctor->parameters.push_back(container);
- ctor->parameters.push_back(broker);
- ctor->parameters.push_back(place);
- this->elements.push_back(ctor);
-
- ctor->statements->Add(new MethodCall("super", 3, container, broker, place));
-}
-
-// =================================================
-class ResultDispatcherClass : public Class
-{
-public:
- ResultDispatcherClass();
- virtual ~ResultDispatcherClass();
-
- void AddMethod(int index, const string& name, Method** method, Variable** param);
-
- bool needed;
- Variable* methodId;
- Variable* callback;
- Method* onResultMethod;
- Variable* resultParam;
- SwitchStatement* methodSwitch;
-
-private:
- void generate_ctor();
- void generate_onResult();
-};
-
-ResultDispatcherClass::ResultDispatcherClass()
- :Class(),
- needed(false)
-{
- this->modifiers = PRIVATE | FINAL;
- this->what = Class::CLASS;
- this->type = new Type("_ResultDispatcher", Type::GENERATED, false, false, false);
- this->interfaces.push_back(RPC_RESULT_HANDLER_TYPE);
-
- // methodId
- this->methodId = new Variable(INT_TYPE, "methodId");
- this->elements.push_back(new Field(PRIVATE, this->methodId));
- this->callback = new Variable(OBJECT_TYPE, "callback");
- this->elements.push_back(new Field(PRIVATE, this->callback));
-
- // methods
- generate_ctor();
- generate_onResult();
-}
-
-ResultDispatcherClass::~ResultDispatcherClass()
-{
-}
-
-void
-ResultDispatcherClass::generate_ctor()
-{
- Variable* methodIdParam = new Variable(INT_TYPE, "methId");
- Variable* callbackParam = new Variable(OBJECT_TYPE, "cbObj");
- Method* ctor = new Method;
- ctor->modifiers = PUBLIC;
- ctor->name = class_name_leaf(this->type->Name());
- ctor->statements = new StatementBlock;
- ctor->parameters.push_back(methodIdParam);
- ctor->parameters.push_back(callbackParam);
- this->elements.push_back(ctor);
-
- ctor->statements->Add(new Assignment(this->methodId, methodIdParam));
- ctor->statements->Add(new Assignment(this->callback, callbackParam));
-}
-
-void
-ResultDispatcherClass::generate_onResult()
-{
- this->onResultMethod = new Method;
- this->onResultMethod->modifiers = PUBLIC;
- this->onResultMethod->returnType = VOID_TYPE;
- this->onResultMethod->returnTypeDimension = 0;
- this->onResultMethod->name = "onResult";
- this->onResultMethod->statements = new StatementBlock;
- this->elements.push_back(this->onResultMethod);
-
- this->resultParam = new Variable(BYTE_TYPE, "result", 1);
- this->onResultMethod->parameters.push_back(this->resultParam);
-
- this->methodSwitch = new SwitchStatement(this->methodId);
- this->onResultMethod->statements->Add(this->methodSwitch);
-}
-
-void
-ResultDispatcherClass::AddMethod(int index, const string& name, Method** method, Variable** param)
-{
- Method* m = new Method;
- m->modifiers = PUBLIC;
- m->returnType = VOID_TYPE;
- m->returnTypeDimension = 0;
- m->name = name;
- m->statements = new StatementBlock;
- *param = new Variable(BYTE_TYPE, "result", 1);
- m->parameters.push_back(*param);
- this->elements.push_back(m);
- *method = m;
-
- Case* c = new Case(format_int(index));
- c->statements->Add(new MethodCall(new LiteralExpression("this"), name, 1, this->resultParam));
- c->statements->Add(new Break());
-
- this->methodSwitch->cases.push_back(c);
-}
-
-// =================================================
-static void
-generate_new_array(Type* t, StatementBlock* addTo, Variable* v, Variable* from)
-{
- fprintf(stderr, "aidl: implement generate_new_array %s:%d\n", __FILE__, __LINE__);
- exit(1);
-}
-
-static void
-generate_create_from_data(Type* t, StatementBlock* addTo, const string& key, Variable* v,
- Variable* data, Variable** cl)
-{
- Expression* k = new StringLiteralExpression(key);
- if (v->dimension == 0) {
- t->CreateFromRpcData(addTo, k, v, data, cl);
- }
- if (v->dimension == 1) {
- //t->ReadArrayFromRpcData(addTo, v, data, cl);
- fprintf(stderr, "aidl: implement generate_create_from_data for arrays%s:%d\n",
- __FILE__, __LINE__);
- }
-}
-
-static void
-generate_write_to_data(Type* t, StatementBlock* addTo, Expression* k, Variable* v, Variable* data)
-{
- if (v->dimension == 0) {
- t->WriteToRpcData(addTo, k, v, data, 0);
- }
- if (v->dimension == 1) {
- //t->WriteArrayToParcel(addTo, v, data);
- fprintf(stderr, "aidl: implement generate_write_to_data for arrays%s:%d\n",
- __FILE__, __LINE__);
- }
-}
-
-// =================================================
-static Type*
-generate_results_method(const method_type* method, RpcProxyClass* proxyClass)
-{
- arg_type* arg;
-
- string resultsMethodName = results_method_name(method->name.data);
- Type* resultsInterfaceType = new Type(results_class_name(method->name.data),
- Type::GENERATED, false, false, false);
-
- if (!method->oneway) {
- Class* resultsClass = new Class;
- resultsClass->modifiers = STATIC | PUBLIC;
- resultsClass->what = Class::INTERFACE;
- resultsClass->type = resultsInterfaceType;
-
- Method* resultMethod = new Method;
- resultMethod->comment = gather_comments(method->comments_token->extra);
- resultMethod->modifiers = PUBLIC;
- resultMethod->returnType = VOID_TYPE;
- resultMethod->returnTypeDimension = 0;
- resultMethod->name = resultsMethodName;
- if (0 != strcmp("void", method->type.type.data)) {
- resultMethod->parameters.push_back(new Variable(NAMES.Search(method->type.type.data),
- "_result", method->type.dimension));
- }
- arg = method->args;
- while (arg != NULL) {
- if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
- resultMethod->parameters.push_back(new Variable(
- NAMES.Search(arg->type.type.data), arg->name.data,
- arg->type.dimension));
- }
- arg = arg->next;
- }
- resultsClass->elements.push_back(resultMethod);
-
- if (resultMethod->parameters.size() > 0) {
- proxyClass->elements.push_back(resultsClass);
- return resultsInterfaceType;
- }
- }
- //delete resultsInterfaceType;
- return NULL;
-}
-
-static void
-generate_proxy_method(const method_type* method, RpcProxyClass* proxyClass,
- ResultDispatcherClass* resultsDispatcherClass, Type* resultsInterfaceType, int index)
-{
- arg_type* arg;
- Method* proxyMethod = new Method;
- proxyMethod->comment = gather_comments(method->comments_token->extra);
- proxyMethod->modifiers = PUBLIC;
- proxyMethod->returnType = VOID_TYPE;
- proxyMethod->returnTypeDimension = 0;
- proxyMethod->name = method->name.data;
- proxyMethod->statements = new StatementBlock;
- proxyClass->elements.push_back(proxyMethod);
-
- // The local variables
- Variable* _data = new Variable(RPC_DATA_TYPE, "_data");
- proxyMethod->statements->Add(new VariableDeclaration(_data, new NewExpression(RPC_DATA_TYPE)));
-
- // Add the arguments
- arg = method->args;
- while (arg != NULL) {
- if (convert_direction(arg->direction.data) & IN_PARAMETER) {
- // Function signature
- Type* t = NAMES.Search(arg->type.type.data);
- Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
- proxyMethod->parameters.push_back(v);
-
- // Input parameter marshalling
- generate_write_to_data(t, proxyMethod->statements,
- new StringLiteralExpression(arg->name.data), v, _data);
- }
- arg = arg->next;
- }
-
- // If there is a results interface for this class
- Expression* resultParameter;
- if (resultsInterfaceType != NULL) {
- // Result interface parameter
- Variable* resultListener = new Variable(resultsInterfaceType, "_result");
- proxyMethod->parameters.push_back(resultListener);
-
- // Add the results dispatcher callback
- resultsDispatcherClass->needed = true;
- resultParameter = new NewExpression(resultsDispatcherClass->type, 2,
- new LiteralExpression(format_int(index)), resultListener);
- } else {
- resultParameter = NULL_VALUE;
- }
-
- // All proxy methods take an error parameter
- Variable* errorListener = new Variable(RPC_ERROR_LISTENER_TYPE, "_errors");
- proxyMethod->parameters.push_back(errorListener);
-
- // Call the broker
- proxyMethod->statements->Add(new MethodCall(new FieldVariable(THIS_VALUE, "_broker"),
- "sendRpc", 5,
- proxyClass->endpoint,
- new StringLiteralExpression(method->name.data),
- new MethodCall(_data, "serialize"),
- resultParameter,
- errorListener));
-}
-
-static void
-generate_result_dispatcher_method(const method_type* method,
- ResultDispatcherClass* resultsDispatcherClass, Type* resultsInterfaceType, int index)
-{
- arg_type* arg;
- Method* dispatchMethod;
- Variable* dispatchParam;
- resultsDispatcherClass->AddMethod(index, method->name.data, &dispatchMethod, &dispatchParam);
-
- Variable* classLoader = NULL;
- Variable* resultData = new Variable(RPC_DATA_TYPE, "resultData");
- dispatchMethod->statements->Add(new VariableDeclaration(resultData,
- new NewExpression(RPC_DATA_TYPE, 1, dispatchParam)));
-
- // The callback method itself
- MethodCall* realCall = new MethodCall(
- new Cast(resultsInterfaceType, new FieldVariable(THIS_VALUE, "callback")),
- results_method_name(method->name.data));
-
- // The return value
- {
- Type* t = NAMES.Search(method->type.type.data);
- if (t != VOID_TYPE) {
- Variable* rv = new Variable(t, "rv");
- dispatchMethod->statements->Add(new VariableDeclaration(rv));
- generate_create_from_data(t, dispatchMethod->statements, "_result", rv,
- resultData, &classLoader);
- realCall->arguments.push_back(rv);
- }
- }
-
- VariableFactory stubArgs("arg");
- arg = method->args;
- while (arg != NULL) {
- if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
- // Unmarshall the results
- Type* t = NAMES.Search(arg->type.type.data);
- Variable* v = stubArgs.Get(t);
- dispatchMethod->statements->Add(new VariableDeclaration(v));
-
- generate_create_from_data(t, dispatchMethod->statements, arg->name.data, v,
- resultData, &classLoader);
-
- // Add the argument to the callback
- realCall->arguments.push_back(v);
- }
- arg = arg->next;
- }
-
- // Call the callback method
- IfStatement* ifst = new IfStatement;
- ifst->expression = new Comparison(new FieldVariable(THIS_VALUE, "callback"), "!=", NULL_VALUE);
- dispatchMethod->statements->Add(ifst);
- ifst->statements->Add(realCall);
-}
-
-static void
-generate_regular_method(const method_type* method, RpcProxyClass* proxyClass,
- EndpointBaseClass* serviceBaseClass, ResultDispatcherClass* resultsDispatcherClass,
- int index)
-{
- arg_type* arg;
-
- // == the callback interface for results ================================
- // the service base class
- Type* resultsInterfaceType = generate_results_method(method, proxyClass);
-
- // == the method in the proxy class =====================================
- generate_proxy_method(method, proxyClass, resultsDispatcherClass, resultsInterfaceType, index);
-
- // == the method in the result dispatcher class =========================
- if (resultsInterfaceType != NULL) {
- generate_result_dispatcher_method(method, resultsDispatcherClass, resultsInterfaceType,
- index);
- }
-
- // == The abstract method that the service developers implement ==========
- Method* decl = new Method;
- decl->comment = gather_comments(method->comments_token->extra);
- decl->modifiers = PUBLIC | ABSTRACT;
- 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;
- }
-
- // Add the default RpcContext param to all methods
- decl->parameters.push_back(new Variable(RPC_CONTEXT_TYPE, "context", 0));
-
- serviceBaseClass->elements.push_back(decl);
-
-
- // == the dispatch method in the service base class ======================
- serviceBaseClass->AddMethod(method);
-}
-
-static void
-generate_event_method(const method_type* method, RpcProxyClass* proxyClass,
- EndpointBaseClass* serviceBaseClass, ListenerClass* listenerClass,
- EventListenerClass* presenterClass, int index)
-{
- arg_type* arg;
- listenerClass->needed = true;
-
- // == the push method in the service base class =========================
- Method* push = new Method;
- push->modifiers = PUBLIC;
- push->name = push_method_name(method->name.data);
- push->statements = new StatementBlock;
- push->returnType = VOID_TYPE;
- serviceBaseClass->elements.push_back(push);
-
- // The local variables
- Variable* _data = new Variable(RPC_DATA_TYPE, "_data");
- push->statements->Add(new VariableDeclaration(_data, new NewExpression(RPC_DATA_TYPE)));
-
- // Add the arguments
- arg = method->args;
- while (arg != NULL) {
- // Function signature
- Type* t = NAMES.Search(arg->type.type.data);
- Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
- push->parameters.push_back(v);
-
- // Input parameter marshalling
- generate_write_to_data(t, push->statements,
- new StringLiteralExpression(arg->name.data), v, _data);
-
- arg = arg->next;
- }
-
- // Send the notifications
- push->statements->Add(new MethodCall("pushEvent", 2,
- new StringLiteralExpression(method->name.data),
- new MethodCall(_data, "serialize")));
-
- // == the event callback dispatcher method ====================================
- presenterClass->AddMethod(method);
-
- // == the event method in the listener base class =====================
- Method* event = new Method;
- event->modifiers = PUBLIC;
- event->name = method->name.data;
- event->statements = new StatementBlock;
- event->returnType = VOID_TYPE;
- listenerClass->elements.push_back(event);
- arg = method->args;
- while (arg != NULL) {
- event->parameters.push_back(new Variable(
- NAMES.Search(arg->type.type.data), arg->name.data,
- arg->type.dimension));
- arg = arg->next;
- }
-
- // Add a final parameter: RpcContext. Contains data about
- // incoming request (e.g., certificate)
- event->parameters.push_back(new Variable(RPC_CONTEXT_TYPE, "context", 0));
-}
-
-static void
-generate_listener_methods(RpcProxyClass* proxyClass, Type* presenterType, Type* listenerType)
-{
- // AndroidAtHomePresenter _presenter;
- // void startListening(Listener listener) {
- // stopListening();
- // _presenter = new Presenter(_broker, listener);
- // _presenter.startListening(_endpoint);
- // }
- // void stopListening() {
- // if (_presenter != null) {
- // _presenter.stopListening();
- // }
- // }
-
- Variable* _presenter = new Variable(presenterType, "_presenter");
- proxyClass->elements.push_back(new Field(PRIVATE, _presenter));
-
- Variable* listener = new Variable(listenerType, "listener");
-
- Method* startListeningMethod = new Method;
- startListeningMethod->modifiers = PUBLIC;
- startListeningMethod->returnType = VOID_TYPE;
- startListeningMethod->name = "startListening";
- startListeningMethod->statements = new StatementBlock;
- startListeningMethod->parameters.push_back(listener);
- proxyClass->elements.push_back(startListeningMethod);
-
- startListeningMethod->statements->Add(new MethodCall(THIS_VALUE, "stopListening"));
- startListeningMethod->statements->Add(new Assignment(_presenter,
- new NewExpression(presenterType, 2, proxyClass->broker, listener)));
- startListeningMethod->statements->Add(new MethodCall(_presenter,
- "startListening", 1, proxyClass->endpoint));
-
- Method* stopListeningMethod = new Method;
- stopListeningMethod->modifiers = PUBLIC;
- stopListeningMethod->returnType = VOID_TYPE;
- stopListeningMethod->name = "stopListening";
- stopListeningMethod->statements = new StatementBlock;
- proxyClass->elements.push_back(stopListeningMethod);
-
- IfStatement* ifst = new IfStatement;
- ifst->expression = new Comparison(_presenter, "!=", NULL_VALUE);
- stopListeningMethod->statements->Add(ifst);
-
- ifst->statements->Add(new MethodCall(_presenter, "stopListening"));
- ifst->statements->Add(new Assignment(_presenter, NULL_VALUE));
-}
-
-Class*
-generate_rpc_interface_class(const interface_type* iface)
-{
- // the proxy class
- InterfaceType* interfaceType = static_cast<InterfaceType*>(
- NAMES.Find(iface->package, iface->name.data));
- RpcProxyClass* proxy = new RpcProxyClass(iface, interfaceType);
-
- // the listener class
- ListenerClass* listener = new ListenerClass(iface);
-
- // the presenter class
- EventListenerClass* presenter = new EventListenerClass(iface, listener->type);
-
- // the service base class
- EndpointBaseClass* base = new EndpointBaseClass(iface);
- proxy->elements.push_back(base);
-
- // the result dispatcher
- ResultDispatcherClass* results = new ResultDispatcherClass();
-
- // all the declared methods of the proxy
- int index = 0;
- interface_item_type* item = iface->interface_items;
- while (item != NULL) {
- if (item->item_type == METHOD_TYPE) {
- if (NAMES.Search(((method_type*)item)->type.type.data) == EVENT_FAKE_TYPE) {
- generate_event_method((method_type*)item, proxy, base, listener, presenter, index);
- } else {
- generate_regular_method((method_type*)item, proxy, base, results, index);
- }
- }
- item = item->next;
- index++;
- }
- presenter->DoneWithMethods();
- base->DoneWithMethods();
-
- // only add this if there are methods with results / out parameters
- if (results->needed) {
- proxy->elements.push_back(results);
- }
- if (listener->needed) {
- proxy->elements.push_back(listener);
- proxy->elements.push_back(presenter);
- generate_listener_methods(proxy, presenter->type, listener->type);
- }
-
- return proxy;
-}
diff --git a/tools/aidl/options.cpp b/tools/aidl/options.cpp
deleted file mode 100644
index 7b2daeb..0000000
--- a/tools/aidl/options.cpp
+++ /dev/null
@@ -1,154 +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->task = COMPILE_AIDL;
- options->failOnParcelable = false;
- options->autoDepFile = false;
-
- // 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 387e37d..0000000
--- a/tools/aidl/options.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef DEVICE_TOOLS_AIDL_H
-#define DEVICE_TOOLS_AIDL_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;
- bool failOnParcelable;
- vector<string> importPaths;
- vector<string> preprocessedFiles;
- string inputFileName;
- string outputFileName;
- string outputBaseFolder;
- string depFileName;
- bool autoDepFile;
-
- 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 // DEVICE_TOOLS_AIDL_H
diff --git a/tools/aidl/options_test.cpp b/tools/aidl/options_test.cpp
deleted file mode 100644
index bd106ce..0000000
--- a/tools/aidl/options_test.cpp
+++ /dev/null
@@ -1,291 +0,0 @@
-#include <iostream>
-#include "options.h"
-
-const bool VERBOSE = false;
-
-using namespace std;
-
-struct Answer {
- const char* argv[8];
- int result;
- const char* systemSearchPath[8];
- const char* localSearchPath[8];
- const char* inputFileName;
- language_t nativeLanguage;
- const char* outputH;
- const char* outputCPP;
- const char* outputJava;
-};
-
-bool
-match_arrays(const char* const*expected, const vector<string> &got)
-{
- int count = 0;
- while (expected[count] != NULL) {
- count++;
- }
- if (got.size() != count) {
- return false;
- }
- for (int i=0; i<count; i++) {
- if (got[i] != expected[i]) {
- return false;
- }
- }
- return true;
-}
-
-void
-print_array(const char* prefix, const char* const*expected)
-{
- while (*expected) {
- cout << prefix << *expected << endl;
- expected++;
- }
-}
-
-void
-print_array(const char* prefix, const vector<string> &got)
-{
- size_t count = got.size();
- for (size_t i=0; i<count; i++) {
- cout << prefix << got[i] << endl;
- }
-}
-
-static int
-test(const Answer& answer)
-{
- int argc = 0;
- while (answer.argv[argc]) {
- argc++;
- }
-
- int err = 0;
-
- Options options;
- int result = parse_options(argc, answer.argv, &options);
-
- // result
- if (((bool)result) != ((bool)answer.result)) {
- cout << "mismatch: result: got " << result << " expected " <<
- answer.result << endl;
- err = 1;
- }
-
- if (result != 0) {
- // if it failed, everything is invalid
- return err;
- }
-
- // systemSearchPath
- if (!match_arrays(answer.systemSearchPath, options.systemSearchPath)) {
- cout << "mismatch: systemSearchPath: got" << endl;
- print_array(" ", options.systemSearchPath);
- cout << " expected" << endl;
- print_array(" ", answer.systemSearchPath);
- err = 1;
- }
-
- // localSearchPath
- if (!match_arrays(answer.localSearchPath, options.localSearchPath)) {
- cout << "mismatch: localSearchPath: got" << endl;
- print_array(" ", options.localSearchPath);
- cout << " expected" << endl;
- print_array(" ", answer.localSearchPath);
- err = 1;
- }
-
- // inputFileName
- if (answer.inputFileName != options.inputFileName) {
- cout << "mismatch: inputFileName: got " << options.inputFileName
- << " expected " << answer.inputFileName << endl;
- err = 1;
- }
-
- // nativeLanguage
- if (answer.nativeLanguage != options.nativeLanguage) {
- cout << "mismatch: nativeLanguage: got " << options.nativeLanguage
- << " expected " << answer.nativeLanguage << endl;
- err = 1;
- }
-
- // outputH
- if (answer.outputH != options.outputH) {
- cout << "mismatch: outputH: got " << options.outputH
- << " expected " << answer.outputH << endl;
- err = 1;
- }
-
- // outputCPP
- if (answer.outputCPP != options.outputCPP) {
- cout << "mismatch: outputCPP: got " << options.outputCPP
- << " expected " << answer.outputCPP << endl;
- err = 1;
- }
-
- // outputJava
- if (answer.outputJava != options.outputJava) {
- cout << "mismatch: outputJava: got " << options.outputJava
- << " expected " << answer.outputJava << endl;
- err = 1;
- }
-
- return err;
-}
-
-const Answer g_tests[] = {
-
- {
- /* argv */ { "test", "-i/moof", "-I/blah", "-Ibleh", "-imoo", "inputFileName.aidl_cpp", NULL, NULL },
- /* result */ 0,
- /* systemSearchPath */ { "/blah", "bleh", NULL, NULL, NULL, NULL, NULL, NULL },
- /* localSearchPath */ { "/moof", "moo", NULL, NULL, NULL, NULL, NULL, NULL },
- /* inputFileName */ "inputFileName.aidl_cpp",
- /* nativeLanguage */ CPP,
- /* outputH */ "",
- /* outputCPP */ "",
- /* outputJava */ ""
- },
-
- {
- /* argv */ { "test", "inputFileName.aidl_cpp", "-oh", "outputH", NULL, NULL, NULL, NULL },
- /* result */ 0,
- /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* inputFileName */ "inputFileName.aidl_cpp",
- /* nativeLanguage */ CPP,
- /* outputH */ "outputH",
- /* outputCPP */ "",
- /* outputJava */ ""
- },
-
- {
- /* argv */ { "test", "inputFileName.aidl_cpp", "-ocpp", "outputCPP", NULL, NULL, NULL, NULL },
- /* result */ 0,
- /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* inputFileName */ "inputFileName.aidl_cpp",
- /* nativeLanguage */ CPP,
- /* outputH */ "",
- /* outputCPP */ "outputCPP",
- /* outputJava */ ""
- },
-
- {
- /* argv */ { "test", "inputFileName.aidl_cpp", "-ojava", "outputJava", NULL, NULL, NULL, NULL },
- /* result */ 0,
- /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* inputFileName */ "inputFileName.aidl_cpp",
- /* nativeLanguage */ CPP,
- /* outputH */ "",
- /* outputCPP */ "",
- /* outputJava */ "outputJava"
- },
-
- {
- /* argv */ { "test", "inputFileName.aidl_cpp", "-oh", "outputH", "-ocpp", "outputCPP", "-ojava", "outputJava" },
- /* result */ 0,
- /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* inputFileName */ "inputFileName.aidl_cpp",
- /* nativeLanguage */ CPP,
- /* outputH */ "outputH",
- /* outputCPP */ "outputCPP",
- /* outputJava */ "outputJava"
- },
-
- {
- /* argv */ { "test", "inputFileName.aidl_cpp", "-oh", "outputH", "-oh", "outputH1", NULL, NULL },
- /* result */ 1,
- /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* inputFileName */ "",
- /* nativeLanguage */ CPP,
- /* outputH */ "",
- /* outputCPP */ "",
- /* outputJava */ ""
- },
-
- {
- /* argv */ { "test", "inputFileName.aidl_cpp", "-ocpp", "outputCPP", "-ocpp", "outputCPP1", NULL, NULL },
- /* result */ 1,
- /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* inputFileName */ "",
- /* nativeLanguage */ CPP,
- /* outputH */ "",
- /* outputCPP */ "",
- /* outputJava */ ""
- },
-
- {
- /* argv */ { "test", "inputFileName.aidl_cpp", "-ojava", "outputJava", "-ojava", "outputJava1", NULL, NULL },
- /* result */ 1,
- /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
- /* inputFileName */ "",
- /* nativeLanguage */ CPP,
- /* outputH */ "",
- /* outputCPP */ "",
- /* outputJava */ ""
- },
-
-};
-
-int
-main(int argc, const char** argv)
-{
- const int count = sizeof(g_tests)/sizeof(g_tests[0]);
- int matches[count];
-
- int result = 0;
- for (int i=0; i<count; i++) {
- if (VERBOSE) {
- cout << endl;
- cout << "---------------------------------------------" << endl;
- const char* const* p = g_tests[i].argv;
- while (*p) {
- cout << " " << *p;
- p++;
- }
- cout << endl;
- cout << "---------------------------------------------" << endl;
- }
- matches[i] = test(g_tests[i]);
- if (VERBOSE) {
- if (0 == matches[i]) {
- cout << "passed" << endl;
- } else {
- cout << "failed" << endl;
- }
- result |= matches[i];
- }
- }
-
- cout << endl;
- cout << "=============================================" << endl;
- cout << "options_test summary" << endl;
- cout << "=============================================" << endl;
-
- if (!result) {
- cout << "passed" << endl;
- } else {
- cout << "failed the following tests:" << endl;
- for (int i=0; i<count; i++) {
- if (matches[i]) {
- cout << " ";
- const char* const* p = g_tests[i].argv;
- while (*p) {
- cout << " " << *p;
- p++;
- }
- cout << endl;
- }
- }
- }
-
- return result;
-}
-
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 2bf94b1..0000000
--- a/tools/aidl/search_path.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef DEVICE_TOOLS_AIDL_SEARCH_PATH_H
-#define DEVICE_TOOLS_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 // DEVICE_TOOLS_AIDL_SEARCH_PATH_H
-
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index a8fd91c..778d1a5 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -74,11 +74,10 @@
// ---- unused implementation of IWindowManager ----
@Override
- public Configuration addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
+ 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) throws RemoteException {
+ Rect arg11, Configuration arg12, boolean arg13) throws RemoteException {
// TODO Auto-generated method stub
- return Configuration.EMPTY;
}
@Override
@@ -315,9 +314,9 @@
}
@Override
- public Configuration setAppTask(IBinder arg0, int arg1, Rect arg2) throws RemoteException {
+ public void setAppTask(IBinder arg0, int arg1, Rect arg2, Configuration arg3)
+ throws RemoteException {
// TODO Auto-generated method stub
- return Configuration.EMPTY;
}
@Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 1f3802e..689e359 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -36,8 +36,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import android.annotation.Nullable;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -139,8 +139,9 @@
private Map<StyleResourceValue, Integer> mStyleToDynamicIdMap;
private int mDynamicIdGenerator = 0x02030000; // Base id for R.style in custom namespace
- // cache for TypedArray generated from IStyleResourceValue object
- private Map<int[], Map<Integer, BridgeTypedArray>> mTypedArrayCache;
+ // cache for TypedArray generated from StyleResourceValue object
+ private Map<int[], Map<List<StyleResourceValue>, Map<Integer, BridgeTypedArray>>>
+ mTypedArrayCache;
private BridgeInflater mBridgeInflater;
private BridgeContentResolver mContentResolver;
@@ -621,31 +622,38 @@
}
}
+ // The map is from
+ // attrs (int[]) -> context's current themes (List<StyleRV>) -> resid (int) -> typed array.
if (mTypedArrayCache == null) {
- mTypedArrayCache = new HashMap<int[], Map<Integer,BridgeTypedArray>>();
-
- Map<Integer, BridgeTypedArray> map = new HashMap<Integer, BridgeTypedArray>();
- mTypedArrayCache.put(attrs, map);
-
- BridgeTypedArray ta = createStyleBasedTypedArray(style, attrs);
- map.put(resid, ta);
-
- return ta;
+ mTypedArrayCache = new IdentityHashMap<int[],
+ Map<List<StyleResourceValue>, Map<Integer, BridgeTypedArray>>>();
}
// get the 2nd map
- Map<Integer, BridgeTypedArray> map = mTypedArrayCache.get(attrs);
- if (map == null) {
- map = new HashMap<Integer, BridgeTypedArray>();
- mTypedArrayCache.put(attrs, map);
+ Map<List<StyleResourceValue>, Map<Integer, BridgeTypedArray>> map2 =
+ mTypedArrayCache.get(attrs);
+ if (map2 == null) {
+ map2 = new HashMap<List<StyleResourceValue>, Map<Integer, BridgeTypedArray>>();
+ mTypedArrayCache.put(attrs, map2);
}
- // get the array from the 2nd map
- BridgeTypedArray ta = map.get(resid);
+ // get the 3rd map
+ List<StyleResourceValue> currentThemes = mRenderResources.getAllThemes();
+ Map<Integer, BridgeTypedArray> map3 = map2.get(currentThemes);
+ if (map3 == null) {
+ map3 = new HashMap<Integer, BridgeTypedArray>();
+ // Create a copy of the list before adding it to the map. This allows reusing the
+ // existing list.
+ currentThemes = new ArrayList<StyleResourceValue>(currentThemes);
+ map2.put(currentThemes, map3);
+ }
+
+ // get the array from the 3rd map
+ BridgeTypedArray ta = map3.get(resid);
if (ta == null) {
ta = createStyleBasedTypedArray(style, attrs);
- map.put(resid, ta);
+ map3.put(resid, ta);
}
return ta;
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/split-select/Android.mk b/tools/split-select/Android.mk
index d9ddf08..239bed5 100644
--- a/tools/split-select/Android.mk
+++ b/tools/split-select/Android.mk
@@ -43,7 +43,6 @@
external/zlib \
frameworks/base/tools
-hostLdLibs :=
hostStaticLibs := \
libaapt \
libandroidfw \
@@ -57,17 +56,13 @@
cFlags := -Wall -Werror
-ifeq ($(HOST_OS),linux)
- hostLdLibs += -lrt -ldl -lpthread
-endif
+hostLdLibs_linux := -lrt -ldl -lpthread
# Statically link libz for MinGW (Win SDK under Linux),
# and dynamically link for all others.
-ifneq ($(strip $(USE_MINGW)),)
- hostStaticLibs += libz
-else
- hostLdLibs += -lz
-endif
+hostStaticLibs_windows := libz
+hostLdLibs_darwin := -lz
+hostLdLibs_linux += -lz
# ==========================================================
@@ -75,11 +70,12 @@
# ==========================================================
include $(CLEAR_VARS)
LOCAL_MODULE := libsplit-select
+LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_SRC_FILES := $(sources)
-LOCAL_C_INCLUDES += $(cIncludes)
-LOCAL_CFLAGS += $(cFlags) -D_DARWIN_UNLIMITED_STREAMS
+LOCAL_C_INCLUDES := $(cIncludes)
+LOCAL_CFLAGS := $(cFlags) -D_DARWIN_UNLIMITED_STREAMS
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -93,10 +89,12 @@
LOCAL_SRC_FILES := $(testSources)
-LOCAL_C_INCLUDES += $(cIncludes)
-LOCAL_STATIC_LIBRARIES += libsplit-select $(hostStaticLibs)
-LOCAL_LDLIBS += $(hostLdLibs)
-LOCAL_CFLAGS += $(cFlags)
+LOCAL_C_INCLUDES := $(cIncludes)
+LOCAL_STATIC_LIBRARIES := libsplit-select $(hostStaticLibs)
+LOCAL_STATIC_LIBRARIES_windows := $(hostStaticLibs_windows)
+LOCAL_LDLIBS_darwin := $(hostLdLibs_darwin)
+LOCAL_LDLIBS_linux := $(hostLdLibs_linux)
+LOCAL_CFLAGS := $(cFlags)
include $(BUILD_HOST_NATIVE_TEST)
@@ -105,13 +103,16 @@
# ==========================================================
include $(CLEAR_VARS)
LOCAL_MODULE := split-select
+LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_SRC_FILES := $(main)
-LOCAL_C_INCLUDES += $(cIncludes)
-LOCAL_STATIC_LIBRARIES += libsplit-select $(hostStaticLibs)
-LOCAL_LDLIBS += $(hostLdLibs)
-LOCAL_CFLAGS += $(cFlags)
+LOCAL_C_INCLUDES := $(cIncludes)
+LOCAL_STATIC_LIBRARIES := libsplit-select $(hostStaticLibs)
+LOCAL_STATIC_LIBRARIES_windows := $(hostStaticLibs_windows)
+LOCAL_LDLIBS_darwin := $(hostLdLibs_darwin)
+LOCAL_LDLIBS_linux := $(hostLdLibs_linux)
+LOCAL_CFLAGS := $(cFlags)
include $(BUILD_HOST_EXECUTABLE)