summaryrefslogtreecommitdiffstats
path: root/core/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/com')
-rw-r--r--core/java/com/android/internal/app/AlertController.java275
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java20
-rw-r--r--core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java1
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl9
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl44
-rw-r--r--core/java/com/android/internal/app/IUsageStats.aidl10
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl32
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractor.aidl35
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractorCallback.aidl32
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractorRequest.aidl (renamed from core/java/com/android/internal/backup/BackupConstants.java)14
-rw-r--r--core/java/com/android/internal/app/IntentForwarderActivity.java113
-rw-r--r--core/java/com/android/internal/app/LocalePicker.java2
-rw-r--r--core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java1
-rw-r--r--core/java/com/android/internal/app/MediaRouteControllerDialog.java4
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java233
-rw-r--r--core/java/com/android/internal/app/ProcessStats.java875
-rw-r--r--core/java/com/android/internal/app/ToolbarActionBar.java450
-rw-r--r--core/java/com/android/internal/app/WindowDecorActionBar.java (renamed from core/java/com/android/internal/app/ActionBarImpl.java)255
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetService.aidl1
-rw-r--r--core/java/com/android/internal/backup/IBackupTransport.aidl2
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java163
-rw-r--r--core/java/com/android/internal/backup/LocalTransportService.java2
-rw-r--r--core/java/com/android/internal/content/PackageMonitor.java7
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java503
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodUtils.java28
-rw-r--r--core/java/com/android/internal/net/NetworkStatsFactory.java33
-rw-r--r--core/java/com/android/internal/net/VpnConfig.java2
-rw-r--r--core/java/com/android/internal/notification/DemoContactNotificationScorer.java188
-rw-r--r--core/java/com/android/internal/notification/NotificationScorer.java27
-rw-r--r--core/java/com/android/internal/os/BatterySipper.java100
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHelper.java838
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java3600
-rw-r--r--core/java/com/android/internal/os/HandlerCaller.java45
-rw-r--r--core/java/com/android/internal/os/PkgUsageStats.aidl20
-rw-r--r--core/java/com/android/internal/os/PkgUsageStats.java94
-rw-r--r--core/java/com/android/internal/os/SomeArgs.java8
-rw-r--r--core/java/com/android/internal/policy/IKeyguardService.aidl25
-rw-r--r--core/java/com/android/internal/policy/IKeyguardServiceConstants.java41
-rw-r--r--core/java/com/android/internal/preference/YesNoPreference.java12
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl14
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl23
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java116
-rw-r--r--core/java/com/android/internal/util/AsyncChannel.java3
-rw-r--r--core/java/com/android/internal/util/GrowingArrayUtils.java196
-rw-r--r--core/java/com/android/internal/util/ImageUtils.java84
-rw-r--r--core/java/com/android/internal/util/NotificationColorUtil.java197
-rw-r--r--core/java/com/android/internal/util/Preconditions.java202
-rw-r--r--core/java/com/android/internal/util/Protocol.java8
-rw-r--r--core/java/com/android/internal/util/VirtualRefBasePtr.java53
-rw-r--r--core/java/com/android/internal/util/XmlUtils.java559
-rw-r--r--core/java/com/android/internal/view/ActionBarPolicy.java2
-rw-r--r--core/java/com/android/internal/view/IInputMethodClient.aidl1
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl5
-rw-r--r--core/java/com/android/internal/view/IInputMethodSession.aidl3
-rw-r--r--core/java/com/android/internal/view/RotationPolicy.java73
-rw-r--r--core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java75
-rw-r--r--core/java/com/android/internal/view/animation/HasNativeInterpolator.java37
-rw-r--r--core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java21
-rw-r--r--core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java36
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuItem.java2
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuItemView.java81
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuPresenter.java756
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuView.java620
-rw-r--r--core/java/com/android/internal/view/menu/IconMenuItemView.java17
-rw-r--r--core/java/com/android/internal/view/menu/ListMenuItemView.java18
-rw-r--r--core/java/com/android/internal/view/menu/ListMenuPresenter.java1
-rw-r--r--core/java/com/android/internal/view/menu/MenuBuilder.java12
-rw-r--r--core/java/com/android/internal/view/menu/MenuItemImpl.java2
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java15
-rw-r--r--core/java/com/android/internal/widget/AbsActionBarView.java32
-rw-r--r--core/java/com/android/internal/widget/ActionBarContainer.java89
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java25
-rw-r--r--core/java/com/android/internal/widget/ActionBarOverlayLayout.java490
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java89
-rw-r--r--core/java/com/android/internal/widget/AutoScrollHelper.java4
-rw-r--r--core/java/com/android/internal/widget/DecorContentParent.java53
-rw-r--r--core/java/com/android/internal/widget/DecorToolbar.java94
-rw-r--r--core/java/com/android/internal/widget/DialogTitle.java11
-rw-r--r--core/java/com/android/internal/widget/FaceUnlockView.java2
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl5
-rw-r--r--core/java/com/android/internal/widget/ILockSettingsObserver.aidl22
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java166
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtilsCache.java243
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java67
-rw-r--r--core/java/com/android/internal/widget/PasswordEntryKeyboard.java5
-rw-r--r--core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java2
-rw-r--r--core/java/com/android/internal/widget/PasswordEntryKeyboardView.java11
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java11
-rw-r--r--core/java/com/android/internal/widget/RotarySelector.java6
-rw-r--r--core/java/com/android/internal/widget/ScrollingTabContainerView.java1
-rw-r--r--core/java/com/android/internal/widget/SizeAdaptiveLayout.java23
-rw-r--r--core/java/com/android/internal/widget/SlidingTab.java3
-rw-r--r--core/java/com/android/internal/widget/SubtitleView.java41
-rw-r--r--core/java/com/android/internal/widget/TextProgressBar.java9
-rw-r--r--core/java/com/android/internal/widget/ToolbarWidgetWrapper.java541
-rw-r--r--core/java/com/android/internal/widget/WaveView.java4
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/GlowPadView.java13
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java1269
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java1
-rw-r--r--core/java/com/android/server/AppWidgetBackupBridge.java63
-rw-r--r--core/java/com/android/server/SystemService.java32
-rw-r--r--core/java/com/android/server/SystemServiceManager.java52
-rw-r--r--core/java/com/android/server/WidgetBackupProvider.java34
-rw-r--r--core/java/com/android/server/net/BaseNetworkObserver.java2
104 files changed, 9720 insertions, 5171 deletions
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 19c0a44..5547a10 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -72,6 +72,8 @@ public class AlertController {
private View mView;
+ private int mViewLayoutResId;
+
private int mViewSpacingLeft;
private int mViewSpacingTop;
@@ -102,7 +104,7 @@ public class AlertController {
private ScrollView mScrollView;
- private int mIconId = -1;
+ private int mIconId = 0;
private Drawable mIcon;
@@ -121,23 +123,30 @@ public class AlertController {
private int mCheckedItem = -1;
private int mAlertDialogLayout;
+ private int mButtonPanelSideLayout;
private int mListLayout;
private int mMultiChoiceItemLayout;
private int mSingleChoiceItemLayout;
private int mListItemLayout;
+ private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;
+
private Handler mHandler;
- View.OnClickListener mButtonHandler = new View.OnClickListener() {
+ private final View.OnClickListener mButtonHandler = new View.OnClickListener() {
+ @Override
public void onClick(View v) {
- Message m = null;
+ final Message m;
if (v == mButtonPositive && mButtonPositiveMessage != null) {
m = Message.obtain(mButtonPositiveMessage);
} else if (v == mButtonNegative && mButtonNegativeMessage != null) {
m = Message.obtain(mButtonNegativeMessage);
} else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
m = Message.obtain(mButtonNeutralMessage);
+ } else {
+ m = null;
}
+
if (m != null) {
m.sendToTarget();
}
@@ -193,6 +202,9 @@ public class AlertController {
mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout,
com.android.internal.R.layout.alert_dialog);
+ mButtonPanelSideLayout = a.getResourceId(
+ com.android.internal.R.styleable.AlertDialog_buttonPanelSideLayout, 0);
+
mListLayout = a.getResourceId(
com.android.internal.R.styleable.AlertDialog_listLayout,
com.android.internal.R.layout.select_dialog);
@@ -234,15 +246,22 @@ public class AlertController {
public void installContent() {
/* We use a custom title so never request a window title */
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
-
- if (mView == null || !canTextInput(mView)) {
- mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
- }
- mWindow.setContentView(mAlertDialogLayout);
+ int contentView = selectContentView();
+ mWindow.setContentView(contentView);
setupView();
setupDecor();
}
+
+ private int selectContentView() {
+ if (mButtonPanelSideLayout == 0) {
+ return mAlertDialogLayout;
+ }
+ if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
+ return mButtonPanelSideLayout;
+ }
+ // TODO: use layout hint side for long messages/lists
+ return mAlertDialogLayout;
+ }
public void setTitle(CharSequence title) {
mTitle = title;
@@ -266,10 +285,20 @@ public class AlertController {
}
/**
+ * Set the view resource to display in the dialog.
+ */
+ public void setView(int layoutResId) {
+ mView = null;
+ mViewLayoutResId = layoutResId;
+ mViewSpacingSpecified = false;
+ }
+
+ /**
* Set the view to display in the dialog.
*/
public void setView(View view) {
mView = view;
+ mViewLayoutResId = 0;
mViewSpacingSpecified = false;
}
@@ -279,6 +308,7 @@ public class AlertController {
public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
int viewSpacingBottom) {
mView = view;
+ mViewLayoutResId = 0;
mViewSpacingSpecified = true;
mViewSpacingLeft = viewSpacingLeft;
mViewSpacingTop = viewSpacingTop;
@@ -287,6 +317,13 @@ public class AlertController {
}
/**
+ * Sets a hint for the best button panel layout.
+ */
+ public void setButtonPanelLayoutHint(int layoutHint) {
+ mButtonPanelLayoutHint = layoutHint;
+ }
+
+ /**
* Sets a click listener or a message to be sent when the button is clicked.
* You only need to pass one of {@code listener} or {@code msg}.
*
@@ -328,25 +365,39 @@ public class AlertController {
}
/**
- * Set resId to 0 if you don't want an icon.
- * @param resId the resourceId of the drawable to use as the icon or 0
- * if you don't want an icon.
+ * Specifies the icon to display next to the alert title.
+ *
+ * @param resId the resource identifier of the drawable to use as the icon,
+ * or 0 for no icon
*/
public void setIcon(int resId) {
+ mIcon = null;
mIconId = resId;
+
if (mIconView != null) {
- if (resId > 0) {
+ if (resId != 0) {
mIconView.setImageResource(mIconId);
- } else if (resId == 0) {
+ } else {
mIconView.setVisibility(View.GONE);
}
}
}
-
+
+ /**
+ * Specifies the icon to display next to the alert title.
+ *
+ * @param icon the drawable to use as the icon or null for no icon
+ */
public void setIcon(Drawable icon) {
mIcon = icon;
- if ((mIconView != null) && (mIcon != null)) {
- mIconView.setImageDrawable(icon);
+ mIconId = 0;
+
+ if (mIconView != null) {
+ if (icon != null) {
+ mIconView.setImageDrawable(icon);
+ } else {
+ mIconView.setVisibility(View.GONE);
+ }
}
}
@@ -430,28 +481,44 @@ public class AlertController {
mWindow.setCloseOnTouchOutsideIfNotSet(true);
}
- FrameLayout customPanel = null;
+ final FrameLayout customPanel = (FrameLayout) mWindow.findViewById(R.id.customPanel);
+ final View customView;
if (mView != null) {
- customPanel = (FrameLayout) mWindow.findViewById(R.id.customPanel);
- FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
- custom.addView(mView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ customView = mView;
+ } else if (mViewLayoutResId != 0) {
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ customView = inflater.inflate(mViewLayoutResId, customPanel, false);
+ } else {
+ customView = null;
+ }
+
+ final boolean hasCustomView = customView != null;
+ if (!hasCustomView || !canTextInput(customView)) {
+ mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ }
+
+ if (hasCustomView) {
+ final FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
+ custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
if (mViewSpacingSpecified) {
- custom.setPadding(mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
- mViewSpacingBottom);
+ custom.setPadding(
+ mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
}
+
if (mListView != null) {
((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
}
} else {
- mWindow.findViewById(R.id.customPanel).setVisibility(View.GONE);
+ customPanel.setVisibility(View.GONE);
}
-
- /* Only display the divider if we have a title and a
- * custom view or a message.
- */
+
+ // Only display the divider if we have a title and a custom view or a
+ // message.
if (hasTitle) {
- View divider = null;
- if (mMessage != null || mView != null || mListView != null) {
+ final View divider;
+ if (mMessage != null || customView != null || mListView != null) {
divider = mWindow.findViewById(R.id.titleDivider);
} else {
divider = mWindow.findViewById(R.id.titleDividerTop);
@@ -461,8 +528,9 @@ public class AlertController {
divider.setVisibility(View.VISIBLE);
}
}
-
- setBackground(topPanel, contentPanel, customPanel, hasButtons, a, hasTitle, buttonPanel);
+
+ setBackground(a, topPanel, contentPanel, customPanel, buttonPanel, hasTitle, hasCustomView,
+ hasButtons);
a.recycle();
}
@@ -480,28 +548,24 @@ public class AlertController {
View titleTemplate = mWindow.findViewById(R.id.title_template);
titleTemplate.setVisibility(View.GONE);
} else {
- final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
-
mIconView = (ImageView) mWindow.findViewById(R.id.icon);
+
+ final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
if (hasTextTitle) {
- /* Display the title if a title is supplied, else hide it */
+ // Display the title if a title is supplied, else hide it.
mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
-
mTitleView.setText(mTitle);
-
- /* Do this last so that if the user has supplied any
- * icons we use them instead of the default ones. If the
- * user has specified 0 then make it disappear.
- */
- if (mIconId > 0) {
+
+ // Do this last so that if the user has supplied any icons we
+ // use them instead of the default ones. If the user has
+ // specified 0 then make it disappear.
+ if (mIconId != 0) {
mIconView.setImageResource(mIconId);
} else if (mIcon != null) {
mIconView.setImageDrawable(mIcon);
- } else if (mIconId == 0) {
-
- /* Apply the padding from the icon to ensure the
- * title is aligned correctly.
- */
+ } else {
+ // Apply the padding from the icon to ensure the title is
+ // aligned correctly.
mTitleView.setPadding(mIconView.getPaddingLeft(),
mIconView.getPaddingTop(),
mIconView.getPaddingRight(),
@@ -509,9 +573,8 @@ public class AlertController {
mIconView.setVisibility(View.GONE);
}
} else {
-
// Hide the title template
- View titleTemplate = mWindow.findViewById(R.id.title_template);
+ final View titleTemplate = mWindow.findViewById(R.id.title_template);
titleTemplate.setVisibility(View.GONE);
mIconView.setVisibility(View.GONE);
topPanel.setVisibility(View.GONE);
@@ -620,11 +683,8 @@ public class AlertController {
}
}
- private void setBackground(LinearLayout topPanel, LinearLayout contentPanel,
- View customPanel, boolean hasButtons, TypedArray a, boolean hasTitle,
- View buttonPanel) {
-
- /* Get all the different background required */
+ private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel,
+ View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) {
int fullDark = 0;
int topDark = 0;
int centerDark = 0;
@@ -645,62 +705,59 @@ public class AlertController {
bottomBright = R.drawable.popup_bottom_bright;
bottomMedium = R.drawable.popup_bottom_medium;
}
- fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark);
- topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark);
- centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark);
- bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark);
- fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright);
topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright);
+ topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark);
centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright);
- bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright);
- bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium);
+ centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark);
+
- /*
- * We now set the background of all of the sections of the alert.
+ /* We now set the background of all of the sections of the alert.
* First collect together each section that is being displayed along
* with whether it is on a light or dark background, then run through
* them setting their backgrounds. This is complicated because we need
* to correctly use the full, top, middle, and bottom graphics depending
* on how many views they are and where they appear.
*/
-
- View[] views = new View[4];
- boolean[] light = new boolean[4];
+
+ final View[] views = new View[4];
+ final boolean[] light = new boolean[4];
View lastView = null;
boolean lastLight = false;
-
+
int pos = 0;
if (hasTitle) {
views[pos] = topPanel;
light[pos] = false;
pos++;
}
-
+
/* The contentPanel displays either a custom text message or
* a ListView. If it's text we should use the dark background
* for ListView we should use the light background. If neither
* are there the contentPanel will be hidden so set it as null.
*/
- views[pos] = (contentPanel.getVisibility() == View.GONE)
- ? null : contentPanel;
+ views[pos] = contentPanel.getVisibility() == View.GONE ? null : contentPanel;
light[pos] = mListView != null;
pos++;
- if (customPanel != null) {
+
+ if (hasCustomView) {
views[pos] = customPanel;
light[pos] = mForceInverseBackground;
pos++;
}
+
if (hasButtons) {
views[pos] = buttonPanel;
light[pos] = true;
}
-
+
boolean setView = false;
- for (pos=0; pos<views.length; pos++) {
- View v = views[pos];
+ for (pos = 0; pos < views.length; pos++) {
+ final View v = views[pos];
if (v == null) {
continue;
}
+
if (lastView != null) {
if (!setView) {
lastView.setBackgroundResource(lastLight ? topBright : topDark);
@@ -709,23 +766,29 @@ public class AlertController {
}
setView = true;
}
+
lastView = v;
lastLight = light[pos];
}
-
+
if (lastView != null) {
if (setView) {
-
- /* ListViews will use the Bright background but buttons use
- * the Medium background.
- */
+ bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright);
+ bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium);
+ bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark);
+
+ // ListViews will use the Bright background, but buttons use the
+ // Medium background.
lastView.setBackgroundResource(
lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
} else {
+ fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright);
+ fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark);
+
lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
}
}
-
+
/* TODO: uncomment section below. The logic for this should be if
* it's a Contextual menu being displayed AND only a Cancel button
* is shown then do this.
@@ -750,12 +813,14 @@ public class AlertController {
mListView.addFooterView(buttonPanel);
*/
// }
-
- if ((mListView != null) && (mAdapter != null)) {
- mListView.setAdapter(mAdapter);
- if (mCheckedItem > -1) {
- mListView.setItemChecked(mCheckedItem, true);
- mListView.setSelection(mCheckedItem);
+
+ final ListView listView = mListView;
+ if (listView != null && mAdapter != null) {
+ listView.setAdapter(mAdapter);
+ final int checkedItem = mCheckedItem;
+ if (checkedItem > -1) {
+ listView.setItemChecked(checkedItem, true);
+ listView.setSelection(checkedItem);
}
}
}
@@ -771,8 +836,13 @@ public class AlertController {
super(context, attrs);
}
- public RecycleListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public RecycleListView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public RecycleListView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
@@ -804,6 +874,7 @@ public class AlertController {
public CharSequence[] mItems;
public ListAdapter mAdapter;
public DialogInterface.OnClickListener mOnClickListener;
+ public int mViewLayoutResId;
public View mView;
public int mViewSpacingLeft;
public int mViewSpacingTop;
@@ -889,8 +960,10 @@ public class AlertController {
} else {
dialog.setView(mView);
}
+ } else if (mViewLayoutResId != 0) {
+ dialog.setView(mViewLayoutResId);
}
-
+
/*
dialog.setCancelable(mCancelable);
dialog.setOnCancelListener(mOnCancelListener);
@@ -953,7 +1026,7 @@ public class AlertController {
? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
if (mCursor == null) {
adapter = (mAdapter != null) ? mAdapter
- : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems);
+ : new CheckedItemAdapter(mContext, layout, R.id.text1, mItems);
} else {
adapter = new SimpleCursorAdapter(mContext, layout,
mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1});
@@ -972,7 +1045,8 @@ public class AlertController {
if (mOnClickListener != null) {
listView.setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView parent, View v, int position, long id) {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
mOnClickListener.onClick(dialog.mDialogInterface, position);
if (!mIsSingleChoice) {
dialog.mDialogInterface.dismiss();
@@ -981,7 +1055,8 @@ public class AlertController {
});
} else if (mOnCheckboxClickListener != null) {
listView.setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView parent, View v, int position, long id) {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
if (mCheckedItems != null) {
mCheckedItems[position] = listView.isItemChecked(position);
}
@@ -1006,4 +1081,20 @@ public class AlertController {
}
}
+ private static class CheckedItemAdapter extends ArrayAdapter<CharSequence> {
+ public CheckedItemAdapter(Context context, int resource, int textViewResourceId,
+ CharSequence[] objects) {
+ super(context, resource, textViewResourceId, objects);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 1eda373..106ac0b 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -33,6 +33,14 @@ public class ChooserActivity extends ResolverActivity {
return;
}
Intent target = (Intent)targetParcelable;
+ if (target != null) {
+ final String action = target.getAction();
+ if (Intent.ACTION_SEND.equals(action) ||
+ Intent.ACTION_SEND_MULTIPLE.equals(action)) {
+ target.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+ Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
+ }
+ }
CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
if (title == null) {
title = getResources().getText(com.android.internal.R.string.chooseActivity);
@@ -43,13 +51,19 @@ public class ChooserActivity extends ResolverActivity {
initialIntents = new Intent[pa.length];
for (int i=0; i<pa.length; i++) {
if (!(pa[i] instanceof Intent)) {
- Log.w("ChooserActivity", "Initial intent #" + i
- + " not an Intent: " + pa[i]);
+ Log.w("ChooserActivity", "Initial intent #" + i + " not an Intent: " + pa[i]);
finish();
super.onCreate(null);
return;
}
- initialIntents[i] = (Intent)pa[i];
+ final Intent in = (Intent) pa[i];
+ final String action = in.getAction();
+ if (Intent.ACTION_SEND.equals(action) ||
+ Intent.ACTION_SEND_MULTIPLE.equals(action)) {
+ in.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+ Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
+ }
+ initialIntents[i] = in;
}
}
super.onCreate(savedInstanceState, target, title, initialIntents, null, false);
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index 3d46cdd..83ad9dc 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -32,7 +32,6 @@ import android.util.TypedValue;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 16c41f3..8e6fa58 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.app;
import android.app.AppOpsManager;
+import android.os.Bundle;
import com.android.internal.app.IAppOpsCallback;
interface IAppOpsService {
@@ -36,4 +37,12 @@ interface IAppOpsService {
List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
void setMode(int code, int uid, String packageName, int mode);
void resetAllModes();
+ int checkAudioOperation(int code, int stream, int uid, String packageName);
+ void setAudioRestriction(int code, int stream, int uid, int mode, in String[] exceptionPackages);
+
+ void setDeviceOwner(String packageName);
+ void setProfileOwner(String packageName, int userHandle);
+ void setUserRestrictions(in Bundle restrictions, int userHandle);
+ void removeUser(int userHandle);
+
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 5413113..0769b08 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -19,23 +19,42 @@ package com.android.internal.app;
import com.android.internal.os.BatteryStatsImpl;
import android.os.WorkSource;
+import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.SignalStrength;
interface IBatteryStats {
- byte[] getStatistics();
- void noteStartWakelock(int uid, int pid, String name, int type);
- void noteStopWakelock(int uid, int pid, String name, int type);
-
- /* DO NOT CHANGE the position of noteStartSensor without updating
- SensorService.cpp */
+ // These first methods are also called by native code, so must
+ // be kept in sync with frameworks/native/include/binder/IBatteryStats.h
void noteStartSensor(int uid, int sensor);
-
- /* DO NOT CHANGE the position of noteStopSensor without updating
- SensorService.cpp */
void noteStopSensor(int uid, int sensor);
- void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, int type);
- void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type);
+ // Remaining methods are only used in Java.
+ byte[] getStatistics();
+
+ // Return the computed amount of time remaining on battery, in milliseconds.
+ // Returns -1 if nothing could be computed.
+ long computeBatteryTimeRemaining();
+
+ // Return the computed amount of time remaining to fully charge, in milliseconds.
+ // Returns -1 if nothing could be computed.
+ long computeChargeTimeRemaining();
+
+ void addIsolatedUid(int isolatedUid, int appUid);
+ void removeIsolatedUid(int isolatedUid, int appUid);
+
+ void noteEvent(int code, String name, int uid);
+
+ void noteStartWakelock(int uid, int pid, String name, String historyName,
+ int type, boolean unimportantForLogging);
+ void noteStopWakelock(int uid, int pid, String name, String historyName, int type);
+
+ void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, String historyName,
+ int type, boolean unimportantForLogging);
+ void noteChangeWakelockFromSource(in WorkSource ws, int pid, String name, String histyoryName,
+ int type, in WorkSource newWs, int newPid, String newName,
+ String newHistoryName, int newType, boolean newUnimportantForLogging);
+ void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, String historyName,
+ int type);
void noteVibratorOn(int uid, long durationMillis);
void noteVibratorOff(int uid);
@@ -45,6 +64,7 @@ interface IBatteryStats {
void noteScreenBrightness(int brightness);
void noteUserActivity(int uid, int event);
void noteInteractive(boolean interactive);
+ void noteMobileRadioPowerState(int powerState, long timestampNs);
void notePhoneOn();
void notePhoneOff();
void notePhoneSignalStrength(in SignalStrength signalStrength);
@@ -55,8 +75,10 @@ interface IBatteryStats {
void noteWifiRunning(in WorkSource ws);
void noteWifiRunningChanged(in WorkSource oldWs, in WorkSource newWs);
void noteWifiStopped(in WorkSource ws);
+ void noteWifiState(int wifiState, String accessPoint);
void noteBluetoothOn();
void noteBluetoothOff();
+ void noteBluetoothState(int bluetoothState);
void noteFullWifiLockAcquired(int uid);
void noteFullWifiLockReleased(int uid);
void noteWifiScanStarted(int uid);
diff --git a/core/java/com/android/internal/app/IUsageStats.aidl b/core/java/com/android/internal/app/IUsageStats.aidl
index 1ea7409..7e7f0e1 100644
--- a/core/java/com/android/internal/app/IUsageStats.aidl
+++ b/core/java/com/android/internal/app/IUsageStats.aidl
@@ -16,13 +16,17 @@
package com.android.internal.app;
+import android.app.UsageStats;
import android.content.ComponentName;
-import com.android.internal.os.PkgUsageStats;
+import android.content.res.Configuration;
+import android.os.ParcelableParcel;
interface IUsageStats {
void noteResumeComponent(in ComponentName componentName);
void notePauseComponent(in ComponentName componentName);
void noteLaunchTime(in ComponentName componentName, int millis);
- PkgUsageStats getPkgUsageStats(in ComponentName componentName);
- PkgUsageStats[] getAllPkgUsageStats();
+ void noteStartConfig(in Configuration config);
+ UsageStats.PackageStats getPkgUsageStats(String callingPkg, in ComponentName componentName);
+ UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg);
+ ParcelableParcel getCurrentStats(String callingPkg);
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
new file mode 100644
index 0000000..98e35dd
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.internal.app;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractor;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+
+interface IVoiceInteractionManagerService {
+ void startSession(IVoiceInteractionService service, in Bundle sessionArgs);
+ boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
+ IVoiceInteractor interactor);
+ int startVoiceActivity(IBinder token, in Intent intent, String resolvedType);
+ void finish(IBinder token);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractor.aidl b/core/java/com/android/internal/app/IVoiceInteractor.aidl
new file mode 100644
index 0000000..2900595
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractor.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.internal.app;
+
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * IPC interface for an application to perform calls through a VoiceInteractor.
+ */
+interface IVoiceInteractor {
+ IVoiceInteractorRequest startConfirmation(String callingPackage,
+ IVoiceInteractorCallback callback, CharSequence prompt, in Bundle extras);
+ IVoiceInteractorRequest startAbortVoice(String callingPackage,
+ IVoiceInteractorCallback callback, CharSequence message, in Bundle extras);
+ IVoiceInteractorRequest startCommand(String callingPackage,
+ IVoiceInteractorCallback callback, String command, in Bundle extras);
+ boolean[] supportsCommands(String callingPackage, in String[] commands);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
new file mode 100644
index 0000000..8dbf9d4
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.internal.app;
+
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * IPC interface for an application to receive callbacks from the voice system.
+ */
+oneway interface IVoiceInteractorCallback {
+ void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
+ in Bundle result);
+ void deliverAbortVoiceResult(IVoiceInteractorRequest request, in Bundle result);
+ void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, in Bundle result);
+ void deliverCancel(IVoiceInteractorRequest request);
+}
diff --git a/core/java/com/android/internal/backup/BackupConstants.java b/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl
index 4c276b7..ce2902d 100644
--- a/core/java/com/android/internal/backup/BackupConstants.java
+++ b/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,15 +14,11 @@
* limitations under the License.
*/
-package com.android.internal.backup;
+package com.android.internal.app;
/**
- * Constants used internally between the backup manager and its transports
+ * IPC interface identifying a request from an application calling through an IVoiceInteractor.
*/
-public class BackupConstants {
- public static final int TRANSPORT_OK = 0;
- public static final int TRANSPORT_ERROR = 1;
- public static final int TRANSPORT_NOT_INITIALIZED = 2;
- public static final int AGENT_ERROR = 3;
- public static final int AGENT_UNKNOWN = 4;
+interface IVoiceInteractorRequest {
+ void cancel();
}
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
new file mode 100644
index 0000000..01e5d40
--- /dev/null
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -0,0 +1,113 @@
+/*
+ * 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.internal.app;
+
+import android.app.Activity;
+import android.app.AppGlobals;
+import android.os.Bundle;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.app.ActivityManagerNative;
+import android.os.RemoteException;
+import android.util.Slog;
+import java.util.List;
+import java.util.Set;
+
+
+
+
+/*
+ * This is used in conjunction with the {@link setCrossProfileIntentFilter} method of
+ * {@link DevicePolicyManager} to enable intents to be passed in and out of a managed profile.
+ */
+
+public class IntentForwarderActivity extends Activity {
+
+ public static String TAG = "IntentForwarderActivity";
+
+ public static String FORWARD_INTENT_TO_USER_OWNER
+ = "com.android.internal.app.ForwardIntentToUserOwner";
+
+ public static String FORWARD_INTENT_TO_MANAGED_PROFILE
+ = "com.android.internal.app.ForwardIntentToManagedProfile";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intentReceived = getIntent();
+
+ String className = intentReceived.getComponent().getClassName();
+ final UserHandle userDest;
+
+ if (className.equals(FORWARD_INTENT_TO_USER_OWNER)) {
+ userDest = UserHandle.OWNER;
+ } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
+ userDest = getManagedProfile();
+ } else {
+ Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
+ userDest = null;
+ }
+ if (userDest == null) { // This covers the case where there is no managed profile.
+ finish();
+ return;
+ }
+ Intent newIntent = new Intent(intentReceived);
+ newIntent.setComponent(null);
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
+ |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+ int callingUserId = getUserId();
+ IPackageManager ipm = AppGlobals.getPackageManager();
+ String resolvedType = newIntent.resolveTypeIfNeeded(getContentResolver());
+ boolean canForward = false;
+ try {
+ canForward = ipm.canForwardTo(newIntent, resolvedType, callingUserId,
+ userDest.getIdentifier());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "PackageManagerService is dead?");
+ }
+ if (canForward) {
+ newIntent.prepareToLeaveUser(callingUserId);
+ startActivityAsUser(newIntent, userDest);
+ } else {
+ Slog.wtf(TAG, "the intent: " + newIntent + "cannot be forwarded from user "
+ + callingUserId + " to user " + userDest.getIdentifier());
+ }
+ finish();
+ }
+
+ /**
+ * Returns the managed profile for this device or null if there is no managed
+ * profile.
+ *
+ * TODO: Remove the assumption that there is only one managed profile
+ * on the device.
+ */
+ private UserHandle getManagedProfile() {
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.USER_OWNER);
+ for (UserInfo userInfo : relatedUsers) {
+ if (userInfo.isManagedProfile()) return new UserHandle(userInfo.id);
+ }
+ Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE
+ + " has been called, but there is no managed profile");
+ return null;
+ }
+}
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 043964f..ec2d654 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -117,7 +117,7 @@ public class LocalePicker extends ListFragment {
/** - TODO: Enable when zz_ZY Pseudolocale is complete
* if (!localeList.contains("zz_ZY")) {
* localeList.add("zz_ZY");
- * }
+ * }
*/
}
String[] locales = new String[localeList.size()];
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
index ae362af..237feed 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
@@ -21,7 +21,6 @@ import android.app.DialogFragment;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
-import android.view.View.OnClickListener;
/**
* Media route chooser dialog fragment.
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
index 8fc99c7..b0e0373 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -256,13 +256,13 @@ public class MediaRouteControllerDialog extends Dialog {
private Drawable getIconDrawable() {
if (mRoute.isConnecting()) {
if (mMediaRouteConnectingDrawable == null) {
- mMediaRouteConnectingDrawable = getContext().getResources().getDrawable(
+ mMediaRouteConnectingDrawable = getContext().getDrawable(
R.drawable.ic_media_route_connecting_holo_dark);
}
return mMediaRouteConnectingDrawable;
} else {
if (mMediaRouteOnDrawable == null) {
- mMediaRouteOnDrawable = getContext().getResources().getDrawable(
+ mMediaRouteOnDrawable = getContext().getDrawable(
R.drawable.ic_media_route_on_holo_dark);
}
return mMediaRouteOnDrawable;
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 40a705c..abd1791 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -18,161 +18,122 @@ package com.android.internal.app;
import android.app.Activity;
import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Color;
import android.graphics.Typeface;
-import android.provider.Settings;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.text.method.AllCapsTransformationMethod;
-import android.text.method.TransformationMethod;
+import android.provider.Settings;
+import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnticipateOvershootInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
-import android.widget.Toast;
public class PlatLogoActivity extends Activity {
- FrameLayout mContent;
- int mCount;
- final Handler mHandler = new Handler();
- static final int BGCOLOR = 0xffed1d24;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- DisplayMetrics metrics = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(metrics);
-
- Typeface bold = Typeface.create("sans-serif", Typeface.BOLD);
- Typeface light = Typeface.create("sans-serif-light", Typeface.NORMAL);
-
- mContent = new FrameLayout(this);
- mContent.setBackgroundColor(0xC0000000);
-
- final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- FrameLayout.LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER;
-
- final ImageView logo = new ImageView(this);
- logo.setImageResource(com.android.internal.R.drawable.platlogo);
- logo.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- logo.setVisibility(View.INVISIBLE);
-
- final View bg = new View(this);
- bg.setBackgroundColor(BGCOLOR);
- bg.setAlpha(0f);
-
- final TextView letter = new TextView(this);
-
- letter.setTypeface(bold);
- letter.setTextSize(300);
- letter.setTextColor(0xFFFFFFFF);
- letter.setGravity(Gravity.CENTER);
- letter.setText(String.valueOf(Build.ID).substring(0, 1));
-
- final int p = (int)(4 * metrics.density);
-
- final TextView tv = new TextView(this);
- if (light != null) tv.setTypeface(light);
- tv.setTextSize(30);
- tv.setPadding(p, p, p, p);
- tv.setTextColor(0xFFFFFFFF);
- tv.setGravity(Gravity.CENTER);
- tv.setTransformationMethod(new AllCapsTransformationMethod(this));
- tv.setText("Android " + Build.VERSION.RELEASE);
- tv.setVisibility(View.INVISIBLE);
-
- mContent.addView(bg);
- mContent.addView(letter, lp);
- mContent.addView(logo, lp);
+ private static class Torso extends FrameLayout {
+ boolean mAnimate = false;
+ TextView mText;
+
+ public Torso(Context context) {
+ this(context, null);
+ }
+ public Torso(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+ public Torso(Context context, AttributeSet attrs, int flags) {
+ super(context, attrs, flags);
+
+ for (int i=0; i<2; i++) {
+ final View v = new View(context);
+ v.setBackgroundColor(i % 2 == 0 ? Color.BLUE : Color.RED);
+ addView(v);
+ }
- final FrameLayout.LayoutParams lp2 = new FrameLayout.LayoutParams(lp);
- lp2.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- lp2.bottomMargin = 10*p;
+ mText = new TextView(context);
+ mText.setTextColor(Color.BLACK);
+ mText.setTextSize(14 /* sp */);
+ mText.setTypeface(Typeface.create("monospace", Typeface.BOLD));
- mContent.addView(tv, lp2);
+ addView(mText, new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ Gravity.BOTTOM | Gravity.LEFT
+ ));
+ }
- mContent.setOnClickListener(new View.OnClickListener() {
- int clicks;
+ private Runnable mRunnable = new Runnable() {
@Override
- public void onClick(View v) {
- clicks++;
- if (clicks >= 6) {
- mContent.performLongClick();
- return;
+ public void run() {
+ mText.setText(String.format("android_%s.flv - build %s",
+ Build.VERSION.CODENAME,
+ Build.VERSION.INCREMENTAL));
+ final int N = getChildCount();
+ final float parentw = getMeasuredWidth();
+ final float parenth = getMeasuredHeight();
+ for (int i=0; i<N; i++) {
+ final View v = getChildAt(i);
+ if (v instanceof TextView) continue;
+
+ final int w = (int) (Math.random() * parentw);
+ final int h = (int) (Math.random() * parenth);
+ v.setLayoutParams(new FrameLayout.LayoutParams(w, h));
+
+ v.setX((float) Math.random() * (parentw - w));
+ v.setY((float) Math.random() * (parenth - h));
}
- letter.animate().cancel();
- final float offset = (int)letter.getRotation() % 360;
- letter.animate()
- .rotationBy((Math.random() > 0.5f ? 360 : -360) - offset)
- .setInterpolator(new DecelerateInterpolator())
- .setDuration(700).start();
- }
- });
- mContent.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- if (logo.getVisibility() != View.VISIBLE) {
- bg.setScaleX(0.01f);
- bg.animate().alpha(1f).scaleX(1f).setStartDelay(500).start();
- letter.animate().alpha(0f).scaleY(0.5f).scaleX(0.5f)
- .rotationBy(360)
- .setInterpolator(new AccelerateInterpolator())
- .setDuration(1000)
- .start();
- logo.setAlpha(0f);
- logo.setVisibility(View.VISIBLE);
- logo.setScaleX(0.5f);
- logo.setScaleY(0.5f);
- logo.animate().alpha(1f).scaleX(1f).scaleY(1f)
- .setDuration(1000).setStartDelay(500)
- .setInterpolator(new AnticipateOvershootInterpolator())
- .start();
- tv.setAlpha(0f);
- tv.setVisibility(View.VISIBLE);
- tv.animate().alpha(1f).setDuration(1000).setStartDelay(1000).start();
- return true;
- }
- return false;
+ if (mAnimate) postDelayed(this, 1000);
}
- });
+ };
+ @Override
+ protected void onAttachedToWindow() {
+ mAnimate = true;
+ post(mRunnable);
+ }
+ @Override
+ protected void onDetachedFromWindow() {
+ mAnimate = false;
+ removeCallbacks(mRunnable);
+ }
+ }
- logo.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- if (Settings.System.getLong(getContentResolver(), Settings.System.EGG_MODE, 0)
- == 0) {
- // For posterity: the moment this user unlocked the easter egg
- Settings.System.putLong(getContentResolver(),
- Settings.System.EGG_MODE,
- System.currentTimeMillis());
- }
- try {
- startActivity(new Intent(Intent.ACTION_MAIN)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
- .addCategory("com.android.internal.category.PLATLOGO"));
- } catch (ActivityNotFoundException ex) {
- android.util.Log.e("PlatLogoActivity", "Couldn't catch a break.");
- }
- finish();
- return true;
- }
- });
-
- setContentView(mContent);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Torso t = new Torso(this);
+ t.setBackgroundColor(Color.WHITE);
+
+ t.getChildAt(0)
+ .setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ final ContentResolver cr = getContentResolver();
+ if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0)
+ == 0) {
+ // For posterity: the moment this user unlocked the easter egg
+ Settings.System.putLong(cr,
+ Settings.System.EGG_MODE,
+ System.currentTimeMillis());
+ }
+ try {
+ startActivity(new Intent(Intent.ACTION_MAIN)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ .addCategory("com.android.internal.category.PLATLOGO"));
+ } catch (ActivityNotFoundException ex) {
+ android.util.Log.e("PlatLogoActivity", "Couldn't catch a break.");
+ }
+ finish();
+ return true;
+ }
+ });
+
+ setContentView(t);
}
}
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index cd90cdb..7e11850 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -29,8 +29,11 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.webkit.WebViewFactory;
-import com.android.internal.util.ArrayUtils;
+
+import com.android.internal.util.GrowingArrayUtils;
+
import dalvik.system.VMRuntime;
+import libcore.util.EmptyArray;
import java.io.IOException;
import java.io.InputStream;
@@ -171,7 +174,7 @@ public final class ProcessStats implements Parcelable {
static final String CSV_SEP = "\t";
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 13;
+ private static final int PARCEL_VERSION = 14;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535453;
@@ -189,7 +192,8 @@ public final class ProcessStats implements Parcelable {
public String mTimePeriodStartClockStr;
public int mFlags;
- public final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
+ public final ProcessMap<SparseArray<PackageState>> mPackages
+ = new ProcessMap<SparseArray<PackageState>>();
public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
public final long[] mMemFactorDurations = new long[ADJ_COUNT];
@@ -227,40 +231,45 @@ public final class ProcessStats implements Parcelable {
}
public void add(ProcessStats other) {
- ArrayMap<String, SparseArray<PackageState>> pkgMap = other.mPackages.getMap();
+ ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = other.mPackages.getMap();
for (int ip=0; ip<pkgMap.size(); ip++) {
- String pkgName = pkgMap.keyAt(ip);
- SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final String pkgName = pkgMap.keyAt(ip);
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- PackageState otherState = uids.valueAt(iu);
- final int NPROCS = otherState.mProcesses.size();
- final int NSRVS = otherState.mServices.size();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState otherProc = otherState.mProcesses.valueAt(iproc);
- if (otherProc.mCommonProcess != otherProc) {
- if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
- + " proc " + otherProc.mName);
- ProcessState thisProc = getProcessStateLocked(pkgName, uid,
- otherProc.mName);
- if (thisProc.mCommonProcess == thisProc) {
- if (DEBUG) Slog.d(TAG, "Existing process is single-package, splitting");
- thisProc.mMultiPackage = true;
- long now = SystemClock.uptimeMillis();
- final PackageState pkgState = getPackageStateLocked(pkgName, uid);
- thisProc = thisProc.clone(thisProc.mPackage, now);
- pkgState.mProcesses.put(thisProc.mName, thisProc);
+ final int uid = uids.keyAt(iu);
+ final SparseArray<PackageState> versions = uids.valueAt(iu);
+ for (int iv=0; iv<versions.size(); iv++) {
+ final int vers = versions.keyAt(iv);
+ final PackageState otherState = versions.valueAt(iv);
+ final int NPROCS = otherState.mProcesses.size();
+ final int NSRVS = otherState.mServices.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState otherProc = otherState.mProcesses.valueAt(iproc);
+ if (otherProc.mCommonProcess != otherProc) {
+ if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
+ + " vers " + vers + " proc " + otherProc.mName);
+ ProcessState thisProc = getProcessStateLocked(pkgName, uid, vers,
+ otherProc.mName);
+ if (thisProc.mCommonProcess == thisProc) {
+ if (DEBUG) Slog.d(TAG, "Existing process is single-package, splitting");
+ thisProc.mMultiPackage = true;
+ long now = SystemClock.uptimeMillis();
+ final PackageState pkgState = getPackageStateLocked(pkgName, uid,
+ vers);
+ thisProc = thisProc.clone(thisProc.mPackage, now);
+ pkgState.mProcesses.put(thisProc.mName, thisProc);
+ }
+ thisProc.add(otherProc);
}
- thisProc.add(otherProc);
}
- }
- for (int isvc=0; isvc<NSRVS; isvc++) {
- ServiceState otherSvc = otherState.mServices.valueAt(isvc);
- if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
- + " service " + otherSvc.mName);
- ServiceState thisSvc = getServiceStateLocked(pkgName, uid,
- otherSvc.mProcessName, otherSvc.mName);
- thisSvc.add(otherSvc);
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ ServiceState otherSvc = otherState.mServices.valueAt(isvc);
+ if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
+ + " service " + otherSvc.mName);
+ ServiceState thisSvc = getServiceStateLocked(pkgName, uid, vers,
+ otherSvc.mProcessName, otherSvc.mName);
+ thisSvc.add(otherSvc);
+ }
}
}
}
@@ -275,9 +284,11 @@ public final class ProcessStats implements Parcelable {
if (DEBUG) Slog.d(TAG, "Adding uid " + uid + " proc " + otherProc.mName);
if (thisProc == null) {
if (DEBUG) Slog.d(TAG, "Creating new process!");
- thisProc = new ProcessState(this, otherProc.mPackage, uid, otherProc.mName);
+ thisProc = new ProcessState(this, otherProc.mPackage, uid, otherProc.mVersion,
+ otherProc.mName);
mProcesses.put(otherProc.mName, uid, thisProc);
- PackageState thisState = getPackageStateLocked(otherProc.mPackage, uid);
+ PackageState thisState = getPackageStateLocked(otherProc.mPackage, uid,
+ otherProc.mVersion);
if (!thisState.mProcesses.containsKey(otherProc.mName)) {
thisState.mProcesses.put(otherProc.mName, thisProc);
}
@@ -440,7 +451,7 @@ public final class ProcessStats implements Parcelable {
}
static void dumpServiceTimeCheckin(PrintWriter pw, String label, String packageName,
- int uid, String serviceName, ServiceState svc, int serviceType, int opCount,
+ int uid, int vers, String serviceName, ServiceState svc, int serviceType, int opCount,
int curState, long curStartTime, long now) {
if (opCount <= 0) {
return;
@@ -451,6 +462,8 @@ public final class ProcessStats implements Parcelable {
pw.print(",");
pw.print(uid);
pw.print(",");
+ pw.print(vers);
+ pw.print(",");
pw.print(serviceName);
pw.print(",");
pw.print(opCount);
@@ -775,7 +788,7 @@ public final class ProcessStats implements Parcelable {
static void dumpProcessSummaryLocked(PrintWriter pw, String prefix,
ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
- long now, long totalTime) {
+ boolean inclUidVers, long now, long totalTime) {
for (int i=procs.size()-1; i>=0; i--) {
ProcessState proc = procs.get(i);
pw.print(prefix);
@@ -783,6 +796,8 @@ public final class ProcessStats implements Parcelable {
pw.print(proc.mName);
pw.print(" / ");
UserHandle.formatUid(pw, proc.mUid);
+ pw.print(" / v");
+ pw.print(proc.mVersion);
pw.println(":");
dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates,
procStates, now, totalTime, true);
@@ -869,6 +884,8 @@ public final class ProcessStats implements Parcelable {
pw.print("process");
pw.print(CSV_SEP);
pw.print("uid");
+ pw.print(CSV_SEP);
+ pw.print("vers");
dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null,
sepMemStates ? memStates : null,
sepProcStates ? procStates : null);
@@ -878,6 +895,8 @@ public final class ProcessStats implements Parcelable {
pw.print(proc.mName);
pw.print(CSV_SEP);
UserHandle.formatUid(pw, proc.mUid);
+ pw.print(CSV_SEP);
+ pw.print(proc.mVersion);
dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates,
sepMemStates, memStates, sepProcStates, procStates, now);
pw.println();
@@ -979,53 +998,88 @@ public final class ProcessStats implements Parcelable {
public void resetSafely() {
if (DEBUG) Slog.d(TAG, "Safely resetting state of " + mTimePeriodStartClockStr);
resetCommon();
- long now = SystemClock.uptimeMillis();
- ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+
+ // First initialize use count of all common processes.
+ final long now = SystemClock.uptimeMillis();
+ final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
for (int ip=procMap.size()-1; ip>=0; ip--) {
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ final SparseArray<ProcessState> uids = procMap.valueAt(ip);
for (int iu=uids.size()-1; iu>=0; iu--) {
- ProcessState ps = uids.valueAt(iu);
- if (ps.isInUse()) {
- uids.valueAt(iu).resetSafely(now);
- } else {
- uids.valueAt(iu).makeDead();
+ uids.valueAt(iu).mTmpNumInUse = 0;
+ }
+ }
+
+ // Next reset or prune all per-package processes, and for the ones that are reset
+ // track this back to the common processes.
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+ for (int ip=pkgMap.size()-1; ip>=0; ip--) {
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ for (int iu=uids.size()-1; iu>=0; iu--) {
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ for (int iv=vpkgs.size()-1; iv>=0; iv--) {
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) {
+ final ProcessState ps = pkgState.mProcesses.valueAt(iproc);
+ if (ps.isInUse()) {
+ ps.resetSafely(now);
+ ps.mCommonProcess.mTmpNumInUse++;
+ ps.mCommonProcess.mTmpFoundSubProc = ps;
+ } else {
+ pkgState.mProcesses.valueAt(iproc).makeDead();
+ pkgState.mProcesses.removeAt(iproc);
+ }
+ }
+ for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) {
+ final ServiceState ss = pkgState.mServices.valueAt(isvc);
+ if (ss.isInUse()) {
+ ss.resetSafely(now);
+ } else {
+ pkgState.mServices.removeAt(isvc);
+ }
+ }
+ if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) {
+ vpkgs.removeAt(iv);
+ }
+ }
+ if (vpkgs.size() <= 0) {
uids.removeAt(iu);
}
}
if (uids.size() <= 0) {
- procMap.removeAt(ip);
+ pkgMap.removeAt(ip);
}
}
- ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
- for (int ip=pkgMap.size()-1; ip>=0; ip--) {
- SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+
+ // Finally prune out any common processes that are no longer in use.
+ for (int ip=procMap.size()-1; ip>=0; ip--) {
+ final SparseArray<ProcessState> uids = procMap.valueAt(ip);
for (int iu=uids.size()-1; iu>=0; iu--) {
- PackageState pkgState = uids.valueAt(iu);
- for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) {
- ProcessState ps = pkgState.mProcesses.valueAt(iproc);
- if (ps.isInUse() || ps.mCommonProcess.isInUse()) {
- pkgState.mProcesses.valueAt(iproc).resetSafely(now);
- } else {
- pkgState.mProcesses.valueAt(iproc).makeDead();
- pkgState.mProcesses.removeAt(iproc);
- }
- }
- for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) {
- ServiceState ss = pkgState.mServices.valueAt(isvc);
- if (ss.isInUse()) {
- pkgState.mServices.valueAt(isvc).resetSafely(now);
+ ProcessState ps = uids.valueAt(iu);
+ if (ps.isInUse() || ps.mTmpNumInUse > 0) {
+ // If this is a process for multiple packages, we could at this point
+ // be back down to one package. In that case, we want to revert back
+ // to a single shared ProcessState. We can do this by converting the
+ // current package-specific ProcessState up to the shared ProcessState,
+ // throwing away the current one we have here (because nobody else is
+ // using it).
+ if (!ps.mActive && ps.mMultiPackage && ps.mTmpNumInUse == 1) {
+ // Here we go...
+ ps = ps.mTmpFoundSubProc;
+ ps.mCommonProcess = ps;
+ uids.setValueAt(iu, ps);
} else {
- pkgState.mServices.removeAt(isvc);
+ ps.resetSafely(now);
}
- }
- if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) {
+ } else {
+ ps.makeDead();
uids.removeAt(iu);
}
}
if (uids.size() <= 0) {
- pkgMap.removeAt(ip);
+ procMap.removeAt(ip);
}
}
+
mStartTime = now;
if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
}
@@ -1054,13 +1108,6 @@ public final class ProcessStats implements Parcelable {
mRuntime = runtime;
}
}
- String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview";
- if (!Objects.equals(webview, mWebView)) {
- changed = true;
- if (update) {
- mWebView = webview;
- }
- }
return changed;
}
@@ -1193,23 +1240,27 @@ public final class ProcessStats implements Parcelable {
uids.valueAt(iu).commitStateTime(now);
}
}
- ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
final int NPKG = pkgMap.size();
for (int ip=0; ip<NPKG; ip++) {
- SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
final int NUID = uids.size();
for (int iu=0; iu<NUID; iu++) {
- PackageState pkgState = uids.valueAt(iu);
- final int NPROCS = pkgState.mProcesses.size();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (proc.mCommonProcess != proc) {
- proc.commitStateTime(now);
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ final int NVERS = vpkgs.size();
+ for (int iv=0; iv<NVERS; iv++) {
+ PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (proc.mCommonProcess != proc) {
+ proc.commitStateTime(now);
+ }
+ }
+ final int NSRVS = pkgState.mServices.size();
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ pkgState.mServices.valueAt(isvc).commitStateTime(now);
}
- }
- final int NSRVS = pkgState.mServices.size();
- for (int isvc=0; isvc<NSRVS; isvc++) {
- pkgState.mServices.valueAt(isvc).commitStateTime(now);
}
}
}
@@ -1239,46 +1290,53 @@ public final class ProcessStats implements Parcelable {
out.writeInt(NPROC);
for (int ip=0; ip<NPROC; ip++) {
writeCommonString(out, procMap.keyAt(ip));
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ final SparseArray<ProcessState> uids = procMap.valueAt(ip);
final int NUID = uids.size();
out.writeInt(NUID);
for (int iu=0; iu<NUID; iu++) {
out.writeInt(uids.keyAt(iu));
- ProcessState proc = uids.valueAt(iu);
+ final ProcessState proc = uids.valueAt(iu);
writeCommonString(out, proc.mPackage);
+ out.writeInt(proc.mVersion);
proc.writeToParcel(out, now);
}
}
out.writeInt(NPKG);
for (int ip=0; ip<NPKG; ip++) {
writeCommonString(out, pkgMap.keyAt(ip));
- SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
final int NUID = uids.size();
out.writeInt(NUID);
for (int iu=0; iu<NUID; iu++) {
out.writeInt(uids.keyAt(iu));
- PackageState pkgState = uids.valueAt(iu);
- final int NPROCS = pkgState.mProcesses.size();
- out.writeInt(NPROCS);
- for (int iproc=0; iproc<NPROCS; iproc++) {
- writeCommonString(out, pkgState.mProcesses.keyAt(iproc));
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (proc.mCommonProcess == proc) {
- // This is the same as the common process we wrote above.
- out.writeInt(0);
- } else {
- // There is separate data for this package's process.
- out.writeInt(1);
- proc.writeToParcel(out, now);
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ final int NVERS = vpkgs.size();
+ out.writeInt(NVERS);
+ for (int iv=0; iv<NVERS; iv++) {
+ out.writeInt(vpkgs.keyAt(iv));
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ out.writeInt(NPROCS);
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ writeCommonString(out, pkgState.mProcesses.keyAt(iproc));
+ final ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (proc.mCommonProcess == proc) {
+ // This is the same as the common process we wrote above.
+ out.writeInt(0);
+ } else {
+ // There is separate data for this package's process.
+ out.writeInt(1);
+ proc.writeToParcel(out, now);
+ }
+ }
+ final int NSRVS = pkgState.mServices.size();
+ out.writeInt(NSRVS);
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ out.writeString(pkgState.mServices.keyAt(isvc));
+ final ServiceState svc = pkgState.mServices.valueAt(isvc);
+ writeCommonString(out, svc.mProcessName);
+ svc.writeToParcel(out, now);
}
- }
- final int NSRVS = pkgState.mServices.size();
- out.writeInt(NSRVS);
- for (int isvc=0; isvc<NSRVS; isvc++) {
- out.writeString(pkgState.mServices.keyAt(isvc));
- ServiceState svc = pkgState.mServices.valueAt(isvc);
- writeCommonString(out, svc.mProcessName);
- svc.writeToParcel(out, now);
}
}
}
@@ -1396,7 +1454,7 @@ public final class ProcessStats implements Parcelable {
}
while (NPROC > 0) {
NPROC--;
- String procName = readCommonString(in, version);
+ final String procName = readCommonString(in, version);
if (procName == null) {
mReadError = "bad process name";
return;
@@ -1408,23 +1466,24 @@ public final class ProcessStats implements Parcelable {
}
while (NUID > 0) {
NUID--;
- int uid = in.readInt();
+ final int uid = in.readInt();
if (uid < 0) {
mReadError = "bad uid: " + uid;
return;
}
- String pkgName = readCommonString(in, version);
+ final String pkgName = readCommonString(in, version);
if (pkgName == null) {
mReadError = "bad process package name";
return;
}
+ final int vers = in.readInt();
ProcessState proc = hadData ? mProcesses.get(procName, uid) : null;
if (proc != null) {
if (!proc.readFromParcel(in, false)) {
return;
}
} else {
- proc = new ProcessState(this, pkgName, uid, procName);
+ proc = new ProcessState(this, pkgName, uid, vers, procName);
if (!proc.readFromParcel(in, true)) {
return;
}
@@ -1444,7 +1503,7 @@ public final class ProcessStats implements Parcelable {
}
while (NPKG > 0) {
NPKG--;
- String pkgName = readCommonString(in, version);
+ final String pkgName = readCommonString(in, version);
if (pkgName == null) {
mReadError = "bad package name";
return;
@@ -1456,83 +1515,98 @@ public final class ProcessStats implements Parcelable {
}
while (NUID > 0) {
NUID--;
- int uid = in.readInt();
+ final int uid = in.readInt();
if (uid < 0) {
mReadError = "bad uid: " + uid;
return;
}
- PackageState pkgState = new PackageState(pkgName, uid);
- mPackages.put(pkgName, uid, pkgState);
- int NPROCS = in.readInt();
- if (NPROCS < 0) {
- mReadError = "bad package process count: " + NPROCS;
+ int NVERS = in.readInt();
+ if (NVERS < 0) {
+ mReadError = "bad versions count: " + NVERS;
return;
}
- while (NPROCS > 0) {
- NPROCS--;
- String procName = readCommonString(in, version);
- if (procName == null) {
- mReadError = "bad package process name";
- return;
+ while (NVERS > 0) {
+ NVERS--;
+ final int vers = in.readInt();
+ PackageState pkgState = new PackageState(pkgName, uid);
+ SparseArray<PackageState> vpkg = mPackages.get(pkgName, uid);
+ if (vpkg == null) {
+ vpkg = new SparseArray<PackageState>();
+ mPackages.put(pkgName, uid, vpkg);
}
- int hasProc = in.readInt();
- if (DEBUG_PARCEL) Slog.d(TAG, "Reading package " + pkgName + " " + uid
- + " process " + procName + " hasProc=" + hasProc);
- ProcessState commonProc = mProcesses.get(procName, uid);
- if (DEBUG_PARCEL) Slog.d(TAG, "Got common proc " + procName + " " + uid
- + ": " + commonProc);
- if (commonProc == null) {
- mReadError = "no common proc: " + procName;
+ vpkg.put(vers, pkgState);
+ int NPROCS = in.readInt();
+ if (NPROCS < 0) {
+ mReadError = "bad package process count: " + NPROCS;
return;
}
- if (hasProc != 0) {
- // The process for this package is unique to the package; we
- // need to load it. We don't need to do anything about it if
- // it is not unique because if someone later looks for it
- // they will find and use it from the global procs.
- ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null;
- if (proc != null) {
- if (!proc.readFromParcel(in, false)) {
- return;
+ while (NPROCS > 0) {
+ NPROCS--;
+ String procName = readCommonString(in, version);
+ if (procName == null) {
+ mReadError = "bad package process name";
+ return;
+ }
+ int hasProc = in.readInt();
+ if (DEBUG_PARCEL) Slog.d(TAG, "Reading package " + pkgName + " " + uid
+ + " process " + procName + " hasProc=" + hasProc);
+ ProcessState commonProc = mProcesses.get(procName, uid);
+ if (DEBUG_PARCEL) Slog.d(TAG, "Got common proc " + procName + " " + uid
+ + ": " + commonProc);
+ if (commonProc == null) {
+ mReadError = "no common proc: " + procName;
+ return;
+ }
+ if (hasProc != 0) {
+ // The process for this package is unique to the package; we
+ // need to load it. We don't need to do anything about it if
+ // it is not unique because if someone later looks for it
+ // they will find and use it from the global procs.
+ ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null;
+ if (proc != null) {
+ if (!proc.readFromParcel(in, false)) {
+ return;
+ }
+ } else {
+ proc = new ProcessState(commonProc, pkgName, uid, vers, procName,
+ 0);
+ if (!proc.readFromParcel(in, true)) {
+ return;
+ }
}
+ if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: "
+ + procName + " " + uid + " " + proc);
+ pkgState.mProcesses.put(procName, proc);
} else {
- proc = new ProcessState(commonProc, pkgName, uid, procName, 0);
- if (!proc.readFromParcel(in, true)) {
- return;
- }
+ if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: "
+ + procName + " " + uid + " " + commonProc);
+ pkgState.mProcesses.put(procName, commonProc);
}
- if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: "
- + procName + " " + uid + " " + proc);
- pkgState.mProcesses.put(procName, proc);
- } else {
- if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: "
- + procName + " " + uid + " " + commonProc);
- pkgState.mProcesses.put(procName, commonProc);
}
- }
- int NSRVS = in.readInt();
- if (NSRVS < 0) {
- mReadError = "bad package service count: " + NSRVS;
- return;
- }
- while (NSRVS > 0) {
- NSRVS--;
- String serviceName = in.readString();
- if (serviceName == null) {
- mReadError = "bad package service name";
+ int NSRVS = in.readInt();
+ if (NSRVS < 0) {
+ mReadError = "bad package service count: " + NSRVS;
return;
}
- String processName = version > 9 ? readCommonString(in, version) : null;
- ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null;
- if (serv == null) {
- serv = new ServiceState(this, pkgName, serviceName, processName, null);
- }
- if (!serv.readFromParcel(in)) {
- return;
+ while (NSRVS > 0) {
+ NSRVS--;
+ String serviceName = in.readString();
+ if (serviceName == null) {
+ mReadError = "bad package service name";
+ return;
+ }
+ String processName = version > 9 ? readCommonString(in, version) : null;
+ ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null;
+ if (serv == null) {
+ serv = new ServiceState(this, pkgName, serviceName, processName, null);
+ }
+ if (!serv.readFromParcel(in)) {
+ return;
+ }
+ if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " service: "
+ + serviceName + " " + uid + " " + serv);
+ pkgState.mServices.put(serviceName, serv);
}
- if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " service: "
- + serviceName + " " + uid + " " + serv);
- pkgState.mServices.put(serviceName, serv);
}
}
}
@@ -1543,21 +1617,10 @@ public final class ProcessStats implements Parcelable {
}
int addLongData(int index, int type, int num) {
- int tableLen = mAddLongTable != null ? mAddLongTable.length : 0;
- if (mAddLongTableSize >= tableLen) {
- int newSize = ArrayUtils.idealIntArraySize(tableLen + 1);
- int[] newTable = new int[newSize];
- if (tableLen > 0) {
- System.arraycopy(mAddLongTable, 0, newTable, 0, tableLen);
- }
- mAddLongTable = newTable;
- }
- if (mAddLongTableSize > 0 && mAddLongTableSize - index != 0) {
- System.arraycopy(mAddLongTable, index, mAddLongTable, index + 1,
- mAddLongTableSize - index);
- }
int off = allocLongData(num);
- mAddLongTable[index] = type | off;
+ mAddLongTable = GrowingArrayUtils.insert(
+ mAddLongTable != null ? mAddLongTable : EmptyArray.INT,
+ mAddLongTableSize, index, type | off);
mAddLongTableSize++;
return off;
}
@@ -1627,30 +1690,36 @@ public final class ProcessStats implements Parcelable {
return ~lo; // value not present
}
- public PackageState getPackageStateLocked(String packageName, int uid) {
- PackageState as = mPackages.get(packageName, uid);
+ public PackageState getPackageStateLocked(String packageName, int uid, int vers) {
+ SparseArray<PackageState> vpkg = mPackages.get(packageName, uid);
+ if (vpkg == null) {
+ vpkg = new SparseArray<PackageState>();
+ mPackages.put(packageName, uid, vpkg);
+ }
+ PackageState as = vpkg.get(vers);
if (as != null) {
return as;
}
as = new PackageState(packageName, uid);
- mPackages.put(packageName, uid, as);
+ vpkg.put(vers, as);
return as;
}
- public ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
- final PackageState pkgState = getPackageStateLocked(packageName, uid);
+ public ProcessState getProcessStateLocked(String packageName, int uid, int vers,
+ String processName) {
+ final PackageState pkgState = getPackageStateLocked(packageName, uid, vers);
ProcessState ps = pkgState.mProcesses.get(processName);
if (ps != null) {
return ps;
}
ProcessState commonProc = mProcesses.get(processName, uid);
if (commonProc == null) {
- commonProc = new ProcessState(this, packageName, uid, processName);
+ commonProc = new ProcessState(this, packageName, uid, vers, processName);
mProcesses.put(processName, uid, commonProc);
if (DEBUG) Slog.d(TAG, "GETPROC created new common " + commonProc);
}
if (!commonProc.mMultiPackage) {
- if (packageName.equals(commonProc.mPackage)) {
+ if (packageName.equals(commonProc.mPackage) && vers == commonProc.mVersion) {
// This common process is not in use by multiple packages, and
// is for the calling package, so we can just use it directly.
ps = commonProc;
@@ -1668,7 +1737,8 @@ public final class ProcessStats implements Parcelable {
long now = SystemClock.uptimeMillis();
// First let's make a copy of the current process state and put
// that under the now unique state for its original package name.
- final PackageState commonPkgState = getPackageStateLocked(commonProc.mPackage, uid);
+ final PackageState commonPkgState = getPackageStateLocked(commonProc.mPackage,
+ uid, commonProc.mVersion);
if (commonPkgState != null) {
ProcessState cloned = commonProc.clone(commonProc.mPackage, now);
if (DEBUG) Slog.d(TAG, "GETPROC setting clone to pkg " + commonProc.mPackage
@@ -1691,13 +1761,13 @@ public final class ProcessStats implements Parcelable {
+ "/" + uid + " for proc " + commonProc.mName);
}
// And now make a fresh new process state for the new package name.
- ps = new ProcessState(commonProc, packageName, uid, processName, now);
+ ps = new ProcessState(commonProc, packageName, uid, vers, processName, now);
if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
}
} else {
// The common process is for multiple packages, we need to create a
// separate object for the per-package data.
- ps = new ProcessState(commonProc, packageName, uid, processName,
+ ps = new ProcessState(commonProc, packageName, uid, vers, processName,
SystemClock.uptimeMillis());
if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
}
@@ -1706,16 +1776,16 @@ public final class ProcessStats implements Parcelable {
return ps;
}
- public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid,
+ public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid, int vers,
String processName, String className) {
- final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid);
+ final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid, vers);
ProcessStats.ServiceState ss = as.mServices.get(className);
if (ss != null) {
if (DEBUG) Slog.d(TAG, "GETSVC: returning existing " + ss);
return ss;
}
final ProcessStats.ProcessState ps = processName != null
- ? getProcessStateLocked(packageName, uid, processName) : null;
+ ? getProcessStateLocked(packageName, uid, vers, processName) : null;
ss = new ProcessStats.ServiceState(this, packageName, className, processName, ps);
as.mServices.put(className, ss);
if (DEBUG) Slog.d(TAG, "GETSVC: creating " + ss + " in " + ps);
@@ -1756,119 +1826,124 @@ public final class ProcessStats implements Parcelable {
boolean dumpAll, boolean activeOnly) {
long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
mStartTime, now);
- ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
boolean printedHeader = false;
boolean sepNeeded = false;
for (int ip=0; ip<pkgMap.size(); ip++) {
final String pkgName = pkgMap.keyAt(ip);
- final SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
for (int iu=0; iu<uids.size(); iu++) {
final int uid = uids.keyAt(iu);
- final PackageState pkgState = uids.valueAt(iu);
- final int NPROCS = pkgState.mProcesses.size();
- final int NSRVS = pkgState.mServices.size();
- final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
- if (!pkgMatch) {
- boolean procMatch = false;
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (reqPackage.equals(proc.mName)) {
- procMatch = true;
- break;
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ for (int iv=0; iv<vpkgs.size(); iv++) {
+ final int vers = vpkgs.keyAt(iv);
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+ if (!pkgMatch) {
+ boolean procMatch = false;
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (reqPackage.equals(proc.mName)) {
+ procMatch = true;
+ break;
+ }
+ }
+ if (!procMatch) {
+ continue;
}
}
- if (!procMatch) {
- continue;
+ if (NPROCS > 0 || NSRVS > 0) {
+ if (!printedHeader) {
+ pw.println("Per-Package Stats:");
+ printedHeader = true;
+ sepNeeded = true;
+ }
+ pw.print(" * "); pw.print(pkgName); pw.print(" / ");
+ UserHandle.formatUid(pw, uid); pw.print(" / v");
+ pw.print(vers); pw.println(":");
}
- }
- if (NPROCS > 0 || NSRVS > 0) {
- if (!printedHeader) {
- pw.println("Per-Package Stats:");
- printedHeader = true;
- sepNeeded = true;
+ if (!dumpSummary || dumpAll) {
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.mName)) {
+ continue;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ pw.print(" (Not active: ");
+ pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")");
+ continue;
+ }
+ pw.print(" Process ");
+ pw.print(pkgState.mProcesses.keyAt(iproc));
+ if (proc.mCommonProcess.mMultiPackage) {
+ pw.print(" (multi, ");
+ } else {
+ pw.print(" (unique, ");
+ }
+ pw.print(proc.mDurationsTableSize);
+ pw.print(" entries)");
+ pw.println(":");
+ dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES);
+ dumpProcessInternalLocked(pw, " ", proc, dumpAll);
+ }
+ } else {
+ ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.mName)) {
+ continue;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ continue;
+ }
+ procs.add(proc);
+ }
+ dumpProcessSummaryLocked(pw, " ", procs, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ NON_CACHED_PROC_STATES, false, now, totalTime);
}
- pw.print(" * "); pw.print(pkgName); pw.print(" / ");
- UserHandle.formatUid(pw, uid); pw.println(":");
- }
- if (!dumpSummary || dumpAll) {
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (!pkgMatch && !reqPackage.equals(proc.mName)) {
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ if (!pkgMatch && !reqPackage.equals(svc.mProcessName)) {
continue;
}
- if (activeOnly && !proc.isInUse()) {
+ if (activeOnly && !svc.isInUse()) {
pw.print(" (Not active: ");
- pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")");
+ pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")");
continue;
}
- pw.print(" Process ");
- pw.print(pkgState.mProcesses.keyAt(iproc));
- if (proc.mCommonProcess.mMultiPackage) {
- pw.print(" (multi, ");
+ if (dumpAll) {
+ pw.print(" Service ");
} else {
- pw.print(" (unique, ");
+ pw.print(" * ");
}
- pw.print(proc.mDurationsTableSize);
- pw.print(" entries)");
+ pw.print(pkgState.mServices.keyAt(isvc));
pw.println(":");
- dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES, now);
- dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES);
- dumpProcessInternalLocked(pw, " ", proc, dumpAll);
- }
- } else {
- ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (!pkgMatch && !reqPackage.equals(proc.mName)) {
- continue;
- }
- if (activeOnly && !proc.isInUse()) {
- continue;
- }
- procs.add(proc);
- }
- dumpProcessSummaryLocked(pw, " ", procs, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- NON_CACHED_PROC_STATES, now, totalTime);
- }
- for (int isvc=0; isvc<NSRVS; isvc++) {
- ServiceState svc = pkgState.mServices.valueAt(isvc);
- if (!pkgMatch && !reqPackage.equals(svc.mProcessName)) {
- continue;
- }
- if (activeOnly && !svc.isInUse()) {
- pw.print(" (Not active: ");
- pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")");
- continue;
- }
- if (dumpAll) {
- pw.print(" Service ");
- } else {
- pw.print(" * ");
- }
- pw.print(pkgState.mServices.keyAt(isvc));
- pw.println(":");
- pw.print(" Process: "); pw.println(svc.mProcessName);
- dumpServiceStats(pw, " ", " ", " ", "Running", svc,
- svc.mRunCount, ServiceState.SERVICE_RUN, svc.mRunState,
- svc.mRunStartTime, now, totalTime, !dumpSummary || dumpAll);
- dumpServiceStats(pw, " ", " ", " ", "Started", svc,
- svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState,
- svc.mStartedStartTime, now, totalTime, !dumpSummary || dumpAll);
- dumpServiceStats(pw, " ", " ", " ", "Bound", svc,
- svc.mBoundCount, ServiceState.SERVICE_BOUND, svc.mBoundState,
- svc.mBoundStartTime, now, totalTime, !dumpSummary || dumpAll);
- dumpServiceStats(pw, " ", " ", " ", "Executing", svc,
- svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState,
- svc.mExecStartTime, now, totalTime, !dumpSummary || dumpAll);
- if (dumpAll) {
- if (svc.mOwner != null) {
- pw.print(" mOwner="); pw.println(svc.mOwner);
- }
- if (svc.mStarted || svc.mRestarting) {
- pw.print(" mStarted="); pw.print(svc.mStarted);
- pw.print(" mRestarting="); pw.println(svc.mRestarting);
+ pw.print(" Process: "); pw.println(svc.mProcessName);
+ dumpServiceStats(pw, " ", " ", " ", "Running", svc,
+ svc.mRunCount, ServiceState.SERVICE_RUN, svc.mRunState,
+ svc.mRunStartTime, now, totalTime, !dumpSummary || dumpAll);
+ dumpServiceStats(pw, " ", " ", " ", "Started", svc,
+ svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState,
+ svc.mStartedStartTime, now, totalTime, !dumpSummary || dumpAll);
+ dumpServiceStats(pw, " ", " ", " ", "Bound", svc,
+ svc.mBoundCount, ServiceState.SERVICE_BOUND, svc.mBoundState,
+ svc.mBoundStartTime, now, totalTime, !dumpSummary || dumpAll);
+ dumpServiceStats(pw, " ", " ", " ", "Executing", svc,
+ svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState,
+ svc.mExecStartTime, now, totalTime, !dumpSummary || dumpAll);
+ if (dumpAll) {
+ if (svc.mOwner != null) {
+ pw.print(" mOwner="); pw.println(svc.mOwner);
+ }
+ if (svc.mStarted || svc.mRestarting) {
+ pw.print(" mStarted="); pw.print(svc.mStarted);
+ pw.print(" mRestarting="); pw.println(svc.mRestarting);
+ }
}
}
}
@@ -2059,7 +2134,7 @@ public final class ProcessStats implements Parcelable {
pw.println(header);
}
dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates,
- sortProcStates, now, totalTime);
+ sortProcStates, true, now, totalTime);
}
}
@@ -2067,23 +2142,27 @@ public final class ProcessStats implements Parcelable {
int[] procStates, int sortProcStates[], long now, String reqPackage,
boolean activeOnly) {
final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
- final ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
for (int ip=0; ip<pkgMap.size(); ip++) {
final String pkgName = pkgMap.keyAt(ip);
- final SparseArray<PackageState> procs = pkgMap.valueAt(ip);
+ final SparseArray<SparseArray<PackageState>> procs = pkgMap.valueAt(ip);
for (int iu=0; iu<procs.size(); iu++) {
- final PackageState state = procs.valueAt(iu);
- final int NPROCS = state.mProcesses.size();
- final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
- for (int iproc=0; iproc<NPROCS; iproc++) {
- final ProcessState proc = state.mProcesses.valueAt(iproc);
- if (!pkgMatch && !reqPackage.equals(proc.mName)) {
- continue;
- }
- if (activeOnly && !proc.isInUse()) {
- continue;
+ final SparseArray<PackageState> vpkgs = procs.valueAt(iu);
+ final int NVERS = vpkgs.size();
+ for (int iv=0; iv<NVERS; iv++) {
+ final PackageState state = vpkgs.valueAt(iv);
+ final int NPROCS = state.mProcesses.size();
+ final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ final ProcessState proc = state.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.mName)) {
+ continue;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ continue;
+ }
+ foundProcs.add(proc.mCommonProcess);
}
- foundProcs.add(proc.mCommonProcess);
}
}
}
@@ -2128,8 +2207,8 @@ public final class ProcessStats implements Parcelable {
public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
final long now = SystemClock.uptimeMillis();
- ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
- pw.println("vers,3");
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+ pw.println("vers,4");
pw.print("period,"); pw.print(mTimePeriodStartClockStr);
pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
@@ -2152,75 +2231,85 @@ public final class ProcessStats implements Parcelable {
pw.println();
pw.print("config,"); pw.print(mRuntime); pw.print(','); pw.println(mWebView);
for (int ip=0; ip<pkgMap.size(); ip++) {
- String pkgName = pkgMap.keyAt(ip);
+ final String pkgName = pkgMap.keyAt(ip);
if (reqPackage != null && !reqPackage.equals(pkgName)) {
continue;
}
- SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- PackageState pkgState = uids.valueAt(iu);
- final int NPROCS = pkgState.mProcesses.size();
- final int NSRVS = pkgState.mServices.size();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- pw.print("pkgproc,");
- pw.print(pkgName);
- pw.print(",");
- pw.print(uid);
- pw.print(",");
- pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
- dumpAllProcessStateCheckin(pw, proc, now);
- pw.println();
- if (proc.mPssTableSize > 0) {
- pw.print("pkgpss,");
+ final int uid = uids.keyAt(iu);
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ for (int iv=0; iv<vpkgs.size(); iv++) {
+ final int vers = vpkgs.keyAt(iv);
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ pw.print("pkgproc,");
pw.print(pkgName);
pw.print(",");
pw.print(uid);
pw.print(",");
- pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
- dumpAllProcessPssCheckin(pw, proc);
- pw.println();
- }
- if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0
- || proc.mNumCachedKill > 0) {
- pw.print("pkgkills,");
- pw.print(pkgName);
- pw.print(",");
- pw.print(uid);
+ pw.print(vers);
pw.print(",");
pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
- pw.print(",");
- pw.print(proc.mNumExcessiveWake);
- pw.print(",");
- pw.print(proc.mNumExcessiveCpu);
- pw.print(",");
- pw.print(proc.mNumCachedKill);
- pw.print(",");
- pw.print(proc.mMinCachedKillPss);
- pw.print(":");
- pw.print(proc.mAvgCachedKillPss);
- pw.print(":");
- pw.print(proc.mMaxCachedKillPss);
+ dumpAllProcessStateCheckin(pw, proc, now);
pw.println();
+ if (proc.mPssTableSize > 0) {
+ pw.print("pkgpss,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(vers);
+ pw.print(",");
+ pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
+ dumpAllProcessPssCheckin(pw, proc);
+ pw.println();
+ }
+ if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0
+ || proc.mNumCachedKill > 0) {
+ pw.print("pkgkills,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(vers);
+ pw.print(",");
+ pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
+ pw.print(",");
+ pw.print(proc.mNumExcessiveWake);
+ pw.print(",");
+ pw.print(proc.mNumExcessiveCpu);
+ pw.print(",");
+ pw.print(proc.mNumCachedKill);
+ pw.print(",");
+ pw.print(proc.mMinCachedKillPss);
+ pw.print(":");
+ pw.print(proc.mAvgCachedKillPss);
+ pw.print(":");
+ pw.print(proc.mMaxCachedKillPss);
+ pw.println();
+ }
+ }
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ String serviceName = collapseString(pkgName,
+ pkgState.mServices.keyAt(isvc));
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ dumpServiceTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName,
+ svc, ServiceState.SERVICE_RUN, svc.mRunCount,
+ svc.mRunState, svc.mRunStartTime, now);
+ dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName,
+ svc, ServiceState.SERVICE_STARTED, svc.mStartedCount,
+ svc.mStartedState, svc.mStartedStartTime, now);
+ dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName,
+ svc, ServiceState.SERVICE_BOUND, svc.mBoundCount,
+ svc.mBoundState, svc.mBoundStartTime, now);
+ dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName,
+ svc, ServiceState.SERVICE_EXEC, svc.mExecCount,
+ svc.mExecState, svc.mExecStartTime, now);
}
- }
- for (int isvc=0; isvc<NSRVS; isvc++) {
- String serviceName = collapseString(pkgName,
- pkgState.mServices.keyAt(isvc));
- ServiceState svc = pkgState.mServices.valueAt(isvc);
- dumpServiceTimeCheckin(pw, "pkgsvc-run", pkgName, uid, serviceName,
- svc, ServiceState.SERVICE_RUN, svc.mRunCount,
- svc.mRunState, svc.mRunStartTime, now);
- dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName,
- svc, ServiceState.SERVICE_STARTED, svc.mStartedCount,
- svc.mStartedState, svc.mStartedStartTime, now);
- dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, serviceName,
- svc, ServiceState.SERVICE_BOUND, svc.mBoundCount,
- svc.mBoundState, svc.mBoundStartTime, now);
- dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, serviceName,
- svc, ServiceState.SERVICE_EXEC, svc.mExecCount,
- svc.mExecState, svc.mExecStartTime, now);
}
}
}
@@ -2364,9 +2453,10 @@ public final class ProcessStats implements Parcelable {
}
public static final class ProcessState extends DurationsTable {
- public final ProcessState mCommonProcess;
+ public ProcessState mCommonProcess;
public final String mPackage;
public final int mUid;
+ public final int mVersion;
//final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT];
int mCurState = STATE_NOTHING;
@@ -2393,16 +2483,19 @@ public final class ProcessStats implements Parcelable {
boolean mDead;
public long mTmpTotalTime;
+ int mTmpNumInUse;
+ ProcessState mTmpFoundSubProc;
/**
* Create a new top-level process state, for the initial case where there is only
* a single package running in a process. The initial state is not running.
*/
- public ProcessState(ProcessStats processStats, String pkg, int uid, String name) {
+ public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) {
super(processStats, name);
mCommonProcess = this;
mPackage = pkg;
mUid = uid;
+ mVersion = vers;
}
/**
@@ -2410,18 +2503,19 @@ public final class ProcessStats implements Parcelable {
* state. The current running state of the top-level process is also copied,
* marked as started running at 'now'.
*/
- public ProcessState(ProcessState commonProcess, String pkg, int uid, String name,
+ public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name,
long now) {
super(commonProcess.mStats, name);
mCommonProcess = commonProcess;
mPackage = pkg;
mUid = uid;
+ mVersion = vers;
mCurState = commonProcess.mCurState;
mStartTime = now;
}
ProcessState clone(String pkg, long now) {
- ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now);
+ ProcessState pnew = new ProcessState(this, pkg, mUid, mVersion, mName, now);
copyDurationsTo(pnew);
if (mPssTable != null) {
mStats.mAddLongTable = new int[mPssTable.length];
@@ -2811,9 +2905,20 @@ public final class ProcessStats implements Parcelable {
// The array map is still pointing to a common process state
// that is now shared across packages. Update it to point to
// the new per-package state.
- ProcessState proc = mStats.mPackages.get(pkgName, mUid).mProcesses.get(mName);
+ SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid);
+ if (vpkg == null) {
+ throw new IllegalStateException("Didn't find package " + pkgName
+ + " / " + mUid);
+ }
+ PackageState pkg = vpkg.get(mVersion);
+ if (pkg == null) {
+ throw new IllegalStateException("Didn't find package " + pkgName
+ + " / " + mUid + " vers " + mVersion);
+ }
+ ProcessState proc = pkg.mProcesses.get(mName);
if (proc == null) {
- throw new IllegalStateException("Didn't create per-package process");
+ throw new IllegalStateException("Didn't create per-package process "
+ + mName + " in pkg " + pkgName + " / " + mUid + " vers " + mVersion);
}
return proc;
}
@@ -2829,18 +2934,26 @@ public final class ProcessStats implements Parcelable {
// are losing whatever data we had in the old process state.
Log.wtf(TAG, "Pulling dead proc: name=" + mName + " pkg=" + mPackage
+ " uid=" + mUid + " common.name=" + mCommonProcess.mName);
- proc = mStats.getProcessStateLocked(proc.mPackage, proc.mUid, proc.mName);
+ proc = mStats.getProcessStateLocked(proc.mPackage, proc.mUid, proc.mVersion,
+ proc.mName);
}
if (proc.mMultiPackage) {
// The array map is still pointing to a common process state
// that is now shared across packages. Update it to point to
// the new per-package state.
- PackageState pkg = mStats.mPackages.get(pkgList.keyAt(index), proc.mUid);
- if (pkg == null) {
+ SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index),
+ proc.mUid);
+ if (vpkg == null) {
throw new IllegalStateException("No existing package "
+ pkgList.keyAt(index) + "/" + proc.mUid
+ " for multi-proc " + proc.mName);
}
+ PackageState pkg = vpkg.get(proc.mVersion);
+ if (pkg == null) {
+ throw new IllegalStateException("No existing package "
+ + pkgList.keyAt(index) + "/" + proc.mUid
+ + " for multi-proc " + proc.mName + " version " + proc.mVersion);
+ }
proc = pkg.mProcesses.get(proc.mName);
if (proc == null) {
throw new IllegalStateException("Didn't create per-package process "
@@ -3014,7 +3127,7 @@ public final class ProcessStats implements Parcelable {
}
public boolean isInUse() {
- return mOwner != null;
+ return mOwner != null || mRestarting;
}
void add(ServiceState other) {
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
new file mode 100644
index 0000000..6056bf2
--- /dev/null
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -0,0 +1,450 @@
+/*
+ * 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.internal.app;
+
+import android.annotation.Nullable;
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
+import android.view.ActionMode;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.SpinnerAdapter;
+import android.widget.Toolbar;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.widget.DecorToolbar;
+import com.android.internal.widget.ToolbarWidgetWrapper;
+
+import java.util.ArrayList;
+
+public class ToolbarActionBar extends ActionBar {
+ private Toolbar mToolbar;
+ private DecorToolbar mDecorToolbar;
+ private Window.Callback mWindowCallback;
+
+ private boolean mLastMenuVisibility;
+ private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
+ new ArrayList<OnMenuVisibilityListener>();
+
+ private final Runnable mMenuInvalidator = new Runnable() {
+ @Override
+ public void run() {
+ populateOptionsMenu();
+ }
+ };
+
+ private final Toolbar.OnMenuItemClickListener mMenuClicker =
+ new Toolbar.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ return mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
+ }
+ };
+
+ public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) {
+ mToolbar = toolbar;
+ mDecorToolbar = new ToolbarWidgetWrapper(toolbar);
+ mWindowCallback = windowCallback;
+ toolbar.setOnMenuItemClickListener(mMenuClicker);
+ mDecorToolbar.setWindowTitle(title);
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ setCustomView(view, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+ }
+
+ @Override
+ public void setCustomView(View view, LayoutParams layoutParams) {
+ view.setLayoutParams(layoutParams);
+ mDecorToolbar.setCustomView(view);
+ }
+
+ @Override
+ public void setCustomView(int resId) {
+ final LayoutInflater inflater = LayoutInflater.from(mToolbar.getContext());
+ setCustomView(inflater.inflate(resId, mToolbar, false));
+ }
+
+ @Override
+ public void setIcon(int resId) {
+ mDecorToolbar.setIcon(resId);
+ }
+
+ @Override
+ public void setIcon(Drawable icon) {
+ mDecorToolbar.setIcon(icon);
+ }
+
+ @Override
+ public void setLogo(int resId) {
+ mDecorToolbar.setLogo(resId);
+ }
+
+ @Override
+ public void setLogo(Drawable logo) {
+ mDecorToolbar.setLogo(logo);
+ }
+
+ @Override
+ public void setStackedBackgroundDrawable(Drawable d) {
+ // This space for rent (do nothing)
+ }
+
+ @Override
+ public void setSplitBackgroundDrawable(Drawable d) {
+ // This space for rent (do nothing)
+ }
+
+ @Override
+ public void setHomeButtonEnabled(boolean enabled) {
+ // If the nav button on a Toolbar is present, it's enabled. No-op.
+ }
+
+ @Override
+ public Context getThemedContext() {
+ return mToolbar.getContext();
+ }
+
+ @Override
+ public boolean isTitleTruncated() {
+ return super.isTitleTruncated();
+ }
+
+ @Override
+ public void setHomeAsUpIndicator(Drawable indicator) {
+ mToolbar.setNavigationIcon(indicator);
+ }
+
+ @Override
+ public void setHomeAsUpIndicator(int resId) {
+ mToolbar.setNavigationIcon(resId);
+ }
+
+ @Override
+ public void setHomeActionContentDescription(CharSequence description) {
+ mToolbar.setNavigationDescription(description);
+ }
+
+ @Override
+ public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) {
+ // Do nothing
+ }
+
+ @Override
+ public void setHomeActionContentDescription(int resId) {
+ mToolbar.setNavigationDescription(resId);
+ }
+
+ @Override
+ public void setShowHideAnimationEnabled(boolean enabled) {
+ // This space for rent; no-op.
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration config) {
+ super.onConfigurationChanged(config);
+ }
+
+ @Override
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ return mToolbar.startActionMode(callback);
+ }
+
+ @Override
+ public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public void setSelectedNavigationItem(int position) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public int getSelectedNavigationIndex() {
+ return -1;
+ }
+
+ @Override
+ public int getNavigationItemCount() {
+ return 0;
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mDecorToolbar.setTitle(title);
+ }
+
+ @Override
+ public void setTitle(int resId) {
+ mDecorToolbar.setTitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
+ }
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {
+ mDecorToolbar.setSubtitle(subtitle);
+ }
+
+ @Override
+ public void setSubtitle(int resId) {
+ mDecorToolbar.setSubtitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
+ }
+
+ @Override
+ public void setDisplayOptions(@DisplayOptions int options) {
+ setDisplayOptions(options, 0xffffffff);
+ }
+
+ @Override
+ public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) {
+ mDecorToolbar.setDisplayOptions((options & mask) |
+ mDecorToolbar.getDisplayOptions() & ~mask);
+ }
+
+ @Override
+ public void setDisplayUseLogoEnabled(boolean useLogo) {
+ setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO);
+ }
+
+ @Override
+ public void setDisplayShowHomeEnabled(boolean showHome) {
+ setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME);
+ }
+
+ @Override
+ public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
+ setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP);
+ }
+
+ @Override
+ public void setDisplayShowTitleEnabled(boolean showTitle) {
+ setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE);
+ }
+
+ @Override
+ public void setDisplayShowCustomEnabled(boolean showCustom) {
+ setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM);
+ }
+
+ @Override
+ public void setBackgroundDrawable(@Nullable Drawable d) {
+ mToolbar.setBackground(d);
+ }
+
+ @Override
+ public View getCustomView() {
+ return mDecorToolbar.getCustomView();
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mToolbar.getTitle();
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mToolbar.getSubtitle();
+ }
+
+ @Override
+ public int getNavigationMode() {
+ return NAVIGATION_MODE_STANDARD;
+ }
+
+ @Override
+ public void setNavigationMode(@NavigationMode int mode) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public int getDisplayOptions() {
+ return mDecorToolbar.getDisplayOptions();
+ }
+
+ @Override
+ public Tab newTab() {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public void addTab(Tab tab) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public void addTab(Tab tab, boolean setSelected) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public void addTab(Tab tab, int position) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public void addTab(Tab tab, int position, boolean setSelected) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public void removeTab(Tab tab) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public void removeTabAt(int position) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public void removeAllTabs() {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public void selectTab(Tab tab) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public Tab getSelectedTab() {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public Tab getTabAt(int index) {
+ throw new UnsupportedOperationException(
+ "Navigation modes are not supported in toolbar action bars");
+ }
+
+ @Override
+ public int getTabCount() {
+ return 0;
+ }
+
+ @Override
+ public int getHeight() {
+ return mToolbar.getHeight();
+ }
+
+ @Override
+ public void show() {
+ // TODO: Consider a better transition for this.
+ // Right now use no automatic transition so that the app can supply one if desired.
+ mToolbar.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void hide() {
+ // TODO: Consider a better transition for this.
+ // Right now use no automatic transition so that the app can supply one if desired.
+ mToolbar.setVisibility(View.GONE);
+ }
+
+ @Override
+ public boolean isShowing() {
+ return mToolbar.getVisibility() == View.VISIBLE;
+ }
+
+ @Override
+ public boolean openOptionsMenu() {
+ return mToolbar.showOverflowMenu();
+ }
+
+ @Override
+ public boolean invalidateOptionsMenu() {
+ mToolbar.removeCallbacks(mMenuInvalidator);
+ mToolbar.postOnAnimation(mMenuInvalidator);
+ return true;
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ if (mToolbar.hasExpandedActionView()) {
+ mToolbar.collapseActionView();
+ return true;
+ }
+ return false;
+ }
+
+ void populateOptionsMenu() {
+ final Menu menu = mToolbar.getMenu();
+ final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null;
+ if (mb != null) {
+ mb.stopDispatchingItemsChanged();
+ }
+ try {
+ menu.clear();
+ if (!mWindowCallback.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) ||
+ !mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) {
+ menu.clear();
+ }
+ } finally {
+ if (mb != null) {
+ mb.startDispatchingItemsChanged();
+ }
+ }
+ }
+
+ @Override
+ public boolean onMenuKeyEvent(KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ openOptionsMenu();
+ }
+ return true;
+ }
+
+ public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.add(listener);
+ }
+
+ public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.remove(listener);
+ }
+
+ public void dispatchMenuVisibilityChanged(boolean isVisible) {
+ if (isVisible == mLastMenuVisibility) {
+ return;
+ }
+ mLastMenuVisibility = isVisible;
+
+ final int count = mMenuVisibilityListeners.size();
+ for (int i = 0; i < count; i++) {
+ mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index ad45894..c0b5b97 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -17,7 +17,12 @@
package com.android.internal.app;
import android.animation.ValueAnimator;
+import android.content.res.TypedArray;
+import android.view.ViewGroup;
import android.view.ViewParent;
+import android.widget.AdapterView;
+import android.widget.Toolbar;
+import com.android.internal.R;
import com.android.internal.view.ActionBarPolicy;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
@@ -26,6 +31,7 @@ import com.android.internal.widget.ActionBarContainer;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.ActionBarOverlayLayout;
import com.android.internal.widget.ActionBarView;
+import com.android.internal.widget.DecorToolbar;
import com.android.internal.widget.ScrollingTabContainerView;
import android.animation.Animator;
@@ -41,8 +47,6 @@ import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.util.Log;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.ContextThemeWrapper;
@@ -51,24 +55,25 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
import android.view.Window;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.widget.SpinnerAdapter;
+import com.android.internal.widget.ToolbarWidgetWrapper;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
- * ActionBarImpl is the ActionBar implementation used
- * by devices of all screen sizes. If it detects a compatible decor,
- * it will split contextual modes across both the ActionBarView at
- * the top of the screen and a horizontal LinearLayout at the bottom
- * which is normally hidden.
+ * WindowDecorActionBar is the ActionBar implementation used
+ * by devices of all screen sizes as part of the window decor layout.
+ * If it detects a compatible decor, it will split contextual modes
+ * across both the ActionBarView at the top of the screen and
+ * a horizontal LinearLayout at the bottom which is normally hidden.
*/
-public class ActionBarImpl extends ActionBar {
- private static final String TAG = "ActionBarImpl";
+public class WindowDecorActionBar extends ActionBar implements
+ ActionBarOverlayLayout.ActionBarVisibilityCallback {
+ private static final String TAG = "WindowDecorActionBar";
private Context mContext;
private Context mThemedContext;
@@ -77,7 +82,7 @@ public class ActionBarImpl extends ActionBar {
private ActionBarOverlayLayout mOverlayLayout;
private ActionBarContainer mContainerView;
- private ActionBarView mActionView;
+ private DecorToolbar mDecorToolbar;
private ActionBarContextView mContextView;
private ActionBarContainer mSplitView;
private View mContentView;
@@ -106,9 +111,6 @@ public class ActionBarImpl extends ActionBar {
private int mContextDisplayMode;
private boolean mHasEmbeddedTabs;
- final Handler mHandler = new Handler();
- Runnable mTabSelector;
-
private int mCurWindowVisibility = View.VISIBLE;
private boolean mContentAnimations = true;
@@ -120,6 +122,7 @@ public class ActionBarImpl extends ActionBar {
private Animator mCurrentShowAnim;
private boolean mShowHideAnimationEnabled;
+ boolean mHideOnContentScroll;
final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
@Override
@@ -136,7 +139,7 @@ public class ActionBarImpl extends ActionBar {
mCurrentShowAnim = null;
completeDeferredDestroyActionMode();
if (mOverlayLayout != null) {
- mOverlayLayout.requestFitSystemWindows();
+ mOverlayLayout.requestApplyInsets();
}
}
};
@@ -158,7 +161,7 @@ public class ActionBarImpl extends ActionBar {
}
};
- public ActionBarImpl(Activity activity) {
+ public WindowDecorActionBar(Activity activity) {
mActivity = activity;
Window window = activity.getWindow();
View decor = window.getDecorView();
@@ -169,7 +172,7 @@ public class ActionBarImpl extends ActionBar {
}
}
- public ActionBarImpl(Dialog dialog) {
+ public WindowDecorActionBar(Dialog dialog) {
mDialog = dialog;
init(dialog.getWindow().getDecorView());
}
@@ -178,19 +181,18 @@ public class ActionBarImpl extends ActionBar {
* Only for edit mode.
* @hide
*/
- public ActionBarImpl(View layout) {
+ public WindowDecorActionBar(View layout) {
assert layout.isInEditMode();
init(layout);
}
private void init(View decor) {
- mContext = decor.getContext();
mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(
- com.android.internal.R.id.action_bar_overlay_layout);
+ com.android.internal.R.id.decor_content_parent);
if (mOverlayLayout != null) {
- mOverlayLayout.setActionBar(this);
+ mOverlayLayout.setActionBarVisibilityCallback(this);
}
- mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
+ mDecorToolbar = getDecorToolbar(decor.findViewById(com.android.internal.R.id.action_bar));
mContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
mContainerView = (ActionBarContainer) decor.findViewById(
@@ -198,17 +200,17 @@ public class ActionBarImpl extends ActionBar {
mSplitView = (ActionBarContainer) decor.findViewById(
com.android.internal.R.id.split_action_bar);
- if (mActionView == null || mContextView == null || mContainerView == null) {
+ if (mDecorToolbar == null || mContextView == null || mContainerView == null) {
throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
"with a compatible window decor layout");
}
- mActionView.setContextView(mContextView);
- mContextDisplayMode = mActionView.isSplitActionBar() ?
+ mContext = mDecorToolbar.getContext();
+ mContextDisplayMode = mDecorToolbar.isSplit() ?
CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
// This was initially read from the action bar style
- final int current = mActionView.getDisplayOptions();
+ final int current = mDecorToolbar.getDisplayOptions();
final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0;
if (homeAsUp) {
mDisplayHomeAsUpSet = true;
@@ -217,6 +219,25 @@ public class ActionBarImpl extends ActionBar {
ActionBarPolicy abp = ActionBarPolicy.get(mContext);
setHomeButtonEnabled(abp.enableHomeButtonByDefault() || homeAsUp);
setHasEmbeddedTabs(abp.hasEmbeddedTabs());
+
+ final TypedArray a = mContext.obtainStyledAttributes(null,
+ com.android.internal.R.styleable.ActionBar,
+ com.android.internal.R.attr.actionBarStyle, 0);
+ if (a.getBoolean(R.styleable.ActionBar_hideOnContentScroll, false)) {
+ setHideOnContentScrollEnabled(true);
+ }
+ a.recycle();
+ }
+
+ private DecorToolbar getDecorToolbar(View view) {
+ if (view instanceof DecorToolbar) {
+ return (DecorToolbar) view;
+ } else if (view instanceof Toolbar) {
+ return ((Toolbar) view).getWrapper();
+ } else {
+ throw new IllegalStateException("Can't make a decor toolbar out of " +
+ view.getClass().getSimpleName());
+ }
}
public void onConfigurationChanged(Configuration newConfig) {
@@ -227,28 +248,25 @@ public class ActionBarImpl extends ActionBar {
mHasEmbeddedTabs = hasEmbeddedTabs;
// Switch tab layout configuration if needed
if (!mHasEmbeddedTabs) {
- mActionView.setEmbeddedTabView(null);
+ mDecorToolbar.setEmbeddedTabView(null);
mContainerView.setTabContainer(mTabScrollView);
} else {
mContainerView.setTabContainer(null);
- mActionView.setEmbeddedTabView(mTabScrollView);
+ mDecorToolbar.setEmbeddedTabView(mTabScrollView);
}
final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
if (mTabScrollView != null) {
if (isInTabMode) {
mTabScrollView.setVisibility(View.VISIBLE);
if (mOverlayLayout != null) {
- mOverlayLayout.requestFitSystemWindows();
+ mOverlayLayout.requestApplyInsets();
}
} else {
mTabScrollView.setVisibility(View.GONE);
}
}
- mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode);
- }
-
- public boolean hasNonEmbeddedTabs() {
- return !mHasEmbeddedTabs && getNavigationMode() == NAVIGATION_MODE_TABS;
+ mDecorToolbar.setCollapsible(!mHasEmbeddedTabs && isInTabMode);
+ mOverlayLayout.setHasNonEmbeddedTabs(!mHasEmbeddedTabs && isInTabMode);
}
private void ensureTabsExist() {
@@ -260,12 +278,12 @@ public class ActionBarImpl extends ActionBar {
if (mHasEmbeddedTabs) {
tabScroller.setVisibility(View.VISIBLE);
- mActionView.setEmbeddedTabView(tabScroller);
+ mDecorToolbar.setEmbeddedTabView(tabScroller);
} else {
if (getNavigationMode() == NAVIGATION_MODE_TABS) {
tabScroller.setVisibility(View.VISIBLE);
if (mOverlayLayout != null) {
- mOverlayLayout.requestFitSystemWindows();
+ mOverlayLayout.requestApplyInsets();
}
} else {
tabScroller.setVisibility(View.GONE);
@@ -283,7 +301,7 @@ public class ActionBarImpl extends ActionBar {
}
}
- public void setWindowVisibility(int visibility) {
+ public void onWindowVisibilityChanged(int visibility) {
mCurWindowVisibility = visibility;
}
@@ -323,7 +341,8 @@ public class ActionBarImpl extends ActionBar {
@Override
public void setCustomView(int resId) {
- setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false));
+ setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId,
+ mDecorToolbar.getViewGroup(), false));
}
@Override
@@ -353,7 +372,7 @@ public class ActionBarImpl extends ActionBar {
@Override
public void setHomeButtonEnabled(boolean enable) {
- mActionView.setHomeButtonEnabled(enable);
+ mDecorToolbar.setHomeButtonEnabled(enable);
}
@Override
@@ -367,12 +386,12 @@ public class ActionBarImpl extends ActionBar {
}
public void setSelectedNavigationItem(int position) {
- switch (mActionView.getNavigationMode()) {
+ switch (mDecorToolbar.getNavigationMode()) {
case NAVIGATION_MODE_TABS:
selectTab(mTabs.get(position));
break;
case NAVIGATION_MODE_LIST:
- mActionView.setDropdownSelectedPosition(position);
+ mDecorToolbar.setDropdownSelectedPosition(position);
break;
default:
throw new IllegalStateException(
@@ -396,26 +415,26 @@ public class ActionBarImpl extends ActionBar {
}
public void setTitle(CharSequence title) {
- mActionView.setTitle(title);
+ mDecorToolbar.setTitle(title);
}
public void setSubtitle(CharSequence subtitle) {
- mActionView.setSubtitle(subtitle);
+ mDecorToolbar.setSubtitle(subtitle);
}
public void setDisplayOptions(int options) {
if ((options & DISPLAY_HOME_AS_UP) != 0) {
mDisplayHomeAsUpSet = true;
}
- mActionView.setDisplayOptions(options);
+ mDecorToolbar.setDisplayOptions(options);
}
public void setDisplayOptions(int options, int mask) {
- final int current = mActionView.getDisplayOptions();
+ final int current = mDecorToolbar.getDisplayOptions();
if ((mask & DISPLAY_HOME_AS_UP) != 0) {
mDisplayHomeAsUpSet = true;
}
- mActionView.setDisplayOptions((options & mask) | (current & ~mask));
+ mDecorToolbar.setDisplayOptions((options & mask) | (current & ~mask));
}
public void setBackgroundDrawable(Drawable d) {
@@ -433,23 +452,23 @@ public class ActionBarImpl extends ActionBar {
}
public View getCustomView() {
- return mActionView.getCustomNavigationView();
+ return mDecorToolbar.getCustomView();
}
public CharSequence getTitle() {
- return mActionView.getTitle();
+ return mDecorToolbar.getTitle();
}
public CharSequence getSubtitle() {
- return mActionView.getSubtitle();
+ return mDecorToolbar.getSubtitle();
}
public int getNavigationMode() {
- return mActionView.getNavigationMode();
+ return mDecorToolbar.getNavigationMode();
}
public int getDisplayOptions() {
- return mActionView.getDisplayOptions();
+ return mDecorToolbar.getDisplayOptions();
}
public ActionMode startActionMode(ActionMode.Callback callback) {
@@ -457,6 +476,7 @@ public class ActionBarImpl extends ActionBar {
mActionMode.finish();
}
+ mOverlayLayout.setHideOnContentScrollEnabled(false);
mContextView.killMode();
ActionModeImpl mode = new ActionModeImpl(callback);
if (mode.dispatchOnCreate()) {
@@ -468,7 +488,7 @@ public class ActionBarImpl extends ActionBar {
if (mSplitView.getVisibility() != View.VISIBLE) {
mSplitView.setVisibility(View.VISIBLE);
if (mOverlayLayout != null) {
- mOverlayLayout.requestFitSystemWindows();
+ mOverlayLayout.requestApplyInsets();
}
}
}
@@ -568,7 +588,7 @@ public class ActionBarImpl extends ActionBar {
return;
}
- final FragmentTransaction trans = mActionView.isInEditMode() ? null :
+ final FragmentTransaction trans = mDecorToolbar.getViewGroup().isInEditMode() ? null :
mActivity.getFragmentManager().beginTransaction().disallowAddToBackStack();
if (mSelectedTab == tab) {
@@ -656,6 +676,35 @@ public class ActionBarImpl extends ActionBar {
}
}
+ @Override
+ public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
+ if (hideOnContentScroll && !mOverlayLayout.isInOverlayMode()) {
+ throw new IllegalStateException("Action bar must be in overlay mode " +
+ "(Window.FEATURE_OVERLAY_ACTION_BAR) to enable hide on content scroll");
+ }
+ mHideOnContentScroll = hideOnContentScroll;
+ mOverlayLayout.setHideOnContentScrollEnabled(hideOnContentScroll);
+ }
+
+ @Override
+ public boolean isHideOnContentScrollEnabled() {
+ return mOverlayLayout.isHideOnContentScrollEnabled();
+ }
+
+ @Override
+ public int getHideOffset() {
+ return mOverlayLayout.getActionBarHideOffset();
+ }
+
+ @Override
+ public void setHideOffset(int offset) {
+ if (offset != 0 && !mOverlayLayout.isInOverlayMode()) {
+ throw new IllegalStateException("Action bar must be in overlay mode " +
+ "(Window.FEATURE_OVERLAY_ACTION_BAR) to set a non-zero hide offset");
+ }
+ mOverlayLayout.setActionBarHideOffset(offset);
+ }
+
private static boolean checkShowingFlags(boolean hiddenByApp, boolean hiddenBySystem,
boolean showingForMode) {
if (showingForMode) {
@@ -741,7 +790,7 @@ public class ActionBarImpl extends ActionBar {
mShowListener.onAnimationEnd(null);
}
if (mOverlayLayout != null) {
- mOverlayLayout.requestFitSystemWindows();
+ mOverlayLayout.requestApplyInsets();
}
}
@@ -785,11 +834,7 @@ public class ActionBarImpl extends ActionBar {
}
public boolean isShowing() {
- return mNowShowing;
- }
-
- public boolean isSystemShowing() {
- return !mHiddenBySystem;
+ return mNowShowing && getHideOffset() < getHeight();
}
void animateToMode(boolean toActionMode) {
@@ -799,13 +844,18 @@ public class ActionBarImpl extends ActionBar {
hideForActionMode();
}
- mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
+ mDecorToolbar.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
- if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) {
+ if (mTabScrollView != null && !mDecorToolbar.hasEmbeddedTabs() &&
+ isCollapsed(mDecorToolbar.getViewGroup())) {
mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
}
}
+ private boolean isCollapsed(View view) {
+ return view == null || view.getVisibility() == View.GONE || view.getMeasuredHeight() == 0;
+ }
+
public Context getThemedContext() {
if (mThemedContext == null) {
TypedValue outValue = new TypedValue();
@@ -825,27 +875,39 @@ public class ActionBarImpl extends ActionBar {
@Override
public boolean isTitleTruncated() {
- return mActionView != null && mActionView.isTitleTruncated();
+ return mDecorToolbar != null && mDecorToolbar.isTitleTruncated();
}
@Override
public void setHomeAsUpIndicator(Drawable indicator) {
- mActionView.setHomeAsUpIndicator(indicator);
+ mDecorToolbar.setNavigationIcon(indicator);
}
@Override
public void setHomeAsUpIndicator(int resId) {
- mActionView.setHomeAsUpIndicator(resId);
+ mDecorToolbar.setNavigationIcon(resId);
}
@Override
public void setHomeActionContentDescription(CharSequence description) {
- mActionView.setHomeActionContentDescription(description);
+ mDecorToolbar.setNavigationContentDescription(description);
}
@Override
public void setHomeActionContentDescription(int resId) {
- mActionView.setHomeActionContentDescription(resId);
+ mDecorToolbar.setNavigationContentDescription(resId);
+ }
+
+ @Override
+ public void onContentScrollStarted() {
+ if (mCurrentShowAnim != null) {
+ mCurrentShowAnim.cancel();
+ mCurrentShowAnim = null;
+ }
+ }
+
+ @Override
+ public void onContentScrollStopped() {
}
/**
@@ -897,7 +959,9 @@ public class ActionBarImpl extends ActionBar {
// Clear out the context mode views after the animation finishes
mContextView.closeMode();
- mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ mDecorToolbar.getViewGroup().sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ mOverlayLayout.setHideOnContentScrollEnabled(mHideOnContentScroll);
mActionMode = null;
}
@@ -1092,7 +1156,7 @@ public class ActionBarImpl extends ActionBar {
@Override
public Tab setIcon(int resId) {
- return setIcon(mContext.getResources().getDrawable(resId));
+ return setIcon(mContext.getDrawable(resId));
}
@Override
@@ -1136,28 +1200,27 @@ public class ActionBarImpl extends ActionBar {
@Override
public void setCustomView(View view) {
- mActionView.setCustomNavigationView(view);
+ mDecorToolbar.setCustomView(view);
}
@Override
public void setCustomView(View view, LayoutParams layoutParams) {
view.setLayoutParams(layoutParams);
- mActionView.setCustomNavigationView(view);
+ mDecorToolbar.setCustomView(view);
}
@Override
public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
- mActionView.setDropdownAdapter(adapter);
- mActionView.setCallback(callback);
+ mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
}
@Override
public int getSelectedNavigationIndex() {
- switch (mActionView.getNavigationMode()) {
+ switch (mDecorToolbar.getNavigationMode()) {
case NAVIGATION_MODE_TABS:
return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
case NAVIGATION_MODE_LIST:
- return mActionView.getDropdownSelectedPosition();
+ return mDecorToolbar.getDropdownSelectedPosition();
default:
return -1;
}
@@ -1165,12 +1228,11 @@ public class ActionBarImpl extends ActionBar {
@Override
public int getNavigationItemCount() {
- switch (mActionView.getNavigationMode()) {
+ switch (mDecorToolbar.getNavigationMode()) {
case NAVIGATION_MODE_TABS:
return mTabs.size();
case NAVIGATION_MODE_LIST:
- SpinnerAdapter adapter = mActionView.getDropdownAdapter();
- return adapter != null ? adapter.getCount() : 0;
+ return mDecorToolbar.getDropdownItemCount();
default:
return 0;
}
@@ -1183,7 +1245,7 @@ public class ActionBarImpl extends ActionBar {
@Override
public void setNavigationMode(int mode) {
- final int oldMode = mActionView.getNavigationMode();
+ final int oldMode = mDecorToolbar.getNavigationMode();
switch (oldMode) {
case NAVIGATION_MODE_TABS:
mSavedTabPosition = getSelectedNavigationIndex();
@@ -1196,7 +1258,7 @@ public class ActionBarImpl extends ActionBar {
mOverlayLayout.requestFitSystemWindows();
}
}
- mActionView.setNavigationMode(mode);
+ mDecorToolbar.setNavigationMode(mode);
switch (mode) {
case NAVIGATION_MODE_TABS:
ensureTabsExist();
@@ -1207,7 +1269,8 @@ public class ActionBarImpl extends ActionBar {
}
break;
}
- mActionView.setCollapsable(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
+ mDecorToolbar.setCollapsible(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
+ mOverlayLayout.setHasNonEmbeddedTabs(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
}
@Override
@@ -1218,30 +1281,30 @@ public class ActionBarImpl extends ActionBar {
@Override
public void setIcon(int resId) {
- mActionView.setIcon(resId);
+ mDecorToolbar.setIcon(resId);
}
@Override
public void setIcon(Drawable icon) {
- mActionView.setIcon(icon);
+ mDecorToolbar.setIcon(icon);
}
public boolean hasIcon() {
- return mActionView.hasIcon();
+ return mDecorToolbar.hasIcon();
}
@Override
public void setLogo(int resId) {
- mActionView.setLogo(resId);
+ mDecorToolbar.setLogo(resId);
}
@Override
public void setLogo(Drawable logo) {
- mActionView.setLogo(logo);
+ mDecorToolbar.setLogo(logo);
}
public boolean hasLogo() {
- return mActionView.hasLogo();
+ return mDecorToolbar.hasLogo();
}
public void setDefaultDisplayHomeAsUpEnabled(boolean enable) {
@@ -1249,4 +1312,24 @@ public class ActionBarImpl extends ActionBar {
setDisplayHomeAsUpEnabled(enable);
}
}
+
+ static class NavItemSelectedListener implements AdapterView.OnItemSelectedListener {
+ private final OnNavigationListener mListener;
+
+ public NavItemSelectedListener(OnNavigationListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ if (mListener != null) {
+ mListener.onNavigationItemSelected(position, id);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Do nothing
+ }
+ }
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 7ddd5d2..5214dd9 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -59,6 +59,5 @@ interface IAppWidgetService {
void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection, int userId);
void unbindRemoteViewsService(int appWidgetId, in Intent intent, int userId);
int[] getAppWidgetIds(in ComponentName provider, int userId);
-
}
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 1e37fd9..d10451b 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -178,7 +178,7 @@ interface IBackupTransport {
/**
* Get the data for the application returned by {@link #nextRestorePackage}.
* @param data An open, writable file into which the backup data should be stored.
- * @return the same error codes as {@link #nextRestorePackage}.
+ * @return the same error codes as {@link #startRestore}.
*/
int getRestoreData(in ParcelFileDescriptor outFd);
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 494bc78..7292116 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -18,34 +18,37 @@ package com.android.internal.backup;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupTransport;
import android.app.backup.RestoreSet;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
import android.os.SELinux;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
import android.util.Log;
import com.android.org.bouncycastle.util.encoders.Base64;
import java.io.File;
-import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
+
+import static android.system.OsConstants.*;
/**
* Backup transport for stashing stuff into a known location on disk, and
* later restoring from there. For testing only.
*/
-public class LocalTransport extends IBackupTransport.Stub {
+public class LocalTransport extends BackupTransport {
private static final String TAG = "LocalTransport";
private static final boolean DEBUG = true;
@@ -55,20 +58,24 @@ public class LocalTransport extends IBackupTransport.Stub {
private static final String TRANSPORT_DESTINATION_STRING
= "Backing up to debug-only private cache";
- // The single hardcoded restore set always has the same (nonzero!) token
- private static final long RESTORE_TOKEN = 1;
+ // The currently-active restore set always has the same (nonzero!) token
+ private static final long CURRENT_SET_TOKEN = 1;
private Context mContext;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
+ private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
+
private PackageInfo[] mRestorePackages = null;
private int mRestorePackage = -1; // Index into mRestorePackages
+ private File mRestoreDataDir;
+ private long mRestoreToken;
public LocalTransport(Context context) {
mContext = context;
- mDataDir.mkdirs();
- if (!SELinux.restorecon(mDataDir)) {
- Log.e(TAG, "SELinux restorecon failed for " + mDataDir);
+ mCurrentSetDir.mkdirs();
+ if (!SELinux.restorecon(mCurrentSetDir)) {
+ Log.e(TAG, "SELinux restorecon failed for " + mCurrentSetDir);
}
}
@@ -96,14 +103,23 @@ public class LocalTransport extends IBackupTransport.Stub {
public int initializeDevice() {
if (DEBUG) Log.v(TAG, "wiping all data");
- deleteContents(mDataDir);
- return BackupConstants.TRANSPORT_OK;
+ deleteContents(mCurrentSetDir);
+ return BackupTransport.TRANSPORT_OK;
}
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
- if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
+ if (DEBUG) {
+ try {
+ StructStat ss = Os.fstat(data.getFileDescriptor());
+ Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
+ + " size=" + ss.st_size);
+ } catch (ErrnoException e) {
+ Log.w(TAG, "Unable to stat input file in performBackup() on "
+ + packageInfo.packageName);
+ }
+ }
- File packageDir = new File(mDataDir, packageInfo.packageName);
+ File packageDir = new File(mCurrentSetDir, packageInfo.packageName);
packageDir.mkdirs();
// Each 'record' in the restore set is kept in its own file, named by
@@ -135,13 +151,22 @@ public class LocalTransport extends IBackupTransport.Stub {
buf = new byte[bufSize];
}
changeSet.readEntityData(buf, 0, dataSize);
- if (DEBUG) Log.v(TAG, " data size " + dataSize);
+ if (DEBUG) {
+ try {
+ long cur = Os.lseek(data.getFileDescriptor(), 0, SEEK_CUR);
+ Log.v(TAG, " read entity data; new pos=" + cur);
+ }
+ catch (ErrnoException e) {
+ Log.w(TAG, "Unable to stat input file in performBackup() on "
+ + packageInfo.packageName);
+ }
+ }
try {
entity.write(buf, 0, dataSize);
} catch (IOException e) {
Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
} finally {
entity.close();
}
@@ -149,11 +174,11 @@ public class LocalTransport extends IBackupTransport.Stub {
entityFile.delete();
}
}
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
} catch (IOException e) {
// oops, something went wrong. abort the operation and return error.
Log.v(TAG, "Exception reading backup input:", e);
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
}
}
@@ -175,7 +200,7 @@ public class LocalTransport extends IBackupTransport.Stub {
public int clearBackupData(PackageInfo packageInfo) {
if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
- File packageDir = new File(mDataDir, packageInfo.packageName);
+ File packageDir = new File(mCurrentSetDir, packageInfo.packageName);
final File[] fileset = packageDir.listFiles();
if (fileset != null) {
for (File f : fileset) {
@@ -183,39 +208,57 @@ public class LocalTransport extends IBackupTransport.Stub {
}
packageDir.delete();
}
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
}
public int finishBackup() {
if (DEBUG) Log.v(TAG, "finishBackup()");
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
}
// Restore handling
- public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
- // one hardcoded restore set
- RestoreSet set = new RestoreSet("Local disk image", "flash", RESTORE_TOKEN);
- RestoreSet[] array = { set };
- return array;
+ static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 };
+ public RestoreSet[] getAvailableRestoreSets() {
+ long[] existing = new long[POSSIBLE_SETS.length + 1];
+ int num = 0;
+
+ // see which possible non-current sets exist, then put the current set at the end
+ for (long token : POSSIBLE_SETS) {
+ if ((new File(mDataDir, Long.toString(token))).exists()) {
+ existing[num++] = token;
+ }
+ }
+ // and always the currently-active set last
+ existing[num++] = CURRENT_SET_TOKEN;
+
+ RestoreSet[] available = new RestoreSet[num];
+ for (int i = 0; i < available.length; i++) {
+ available[i] = new RestoreSet("Local disk image", "flash", existing[i]);
+ }
+ return available;
}
public long getCurrentRestoreSet() {
- // The hardcoded restore set always has the same token
- return RESTORE_TOKEN;
+ // The current restore set always has the same token
+ return CURRENT_SET_TOKEN;
}
public int startRestore(long token, PackageInfo[] packages) {
if (DEBUG) Log.v(TAG, "start restore " + token);
mRestorePackages = packages;
mRestorePackage = -1;
- return BackupConstants.TRANSPORT_OK;
+ mRestoreToken = token;
+ mRestoreDataDir = new File(mDataDir, Long.toString(token));
+ return BackupTransport.TRANSPORT_OK;
}
public String nextRestorePackage() {
if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
while (++mRestorePackage < mRestorePackages.length) {
String name = mRestorePackages[mRestorePackage].packageName;
- if (new File(mDataDir, name).isDirectory()) {
+ // skip packages where we have a data dir but no actual contents
+ String[] contents = (new File(mRestoreDataDir, name)).list();
+ if (contents != null && contents.length > 0) {
if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name);
return name;
}
@@ -228,39 +271,75 @@ public class LocalTransport extends IBackupTransport.Stub {
public int getRestoreData(ParcelFileDescriptor outFd) {
if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
- File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
+ File packageDir = new File(mRestoreDataDir, mRestorePackages[mRestorePackage].packageName);
// The restore set is the concatenation of the individual record blobs,
- // each of which is a file in the package's directory
- File[] blobs = packageDir.listFiles();
+ // each of which is a file in the package's directory. We return the
+ // data in lexical order sorted by key, so that apps which use synthetic
+ // keys like BLOB_1, BLOB_2, etc will see the date in the most obvious
+ // order.
+ ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
if (blobs == null) { // nextRestorePackage() ensures the dir exists, so this is an error
- Log.e(TAG, "Error listing directory: " + packageDir);
- return BackupConstants.TRANSPORT_ERROR;
+ Log.e(TAG, "No keys for package: " + packageDir);
+ return BackupTransport.TRANSPORT_ERROR;
}
// We expect at least some data if the directory exists in the first place
- if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files");
+ if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.size() + " key files");
BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
try {
- for (File f : blobs) {
+ for (DecodedFilename keyEntry : blobs) {
+ File f = keyEntry.file;
FileInputStream in = new FileInputStream(f);
try {
int size = (int) f.length();
byte[] buf = new byte[size];
in.read(buf);
- String key = new String(Base64.decode(f.getName()));
- if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size);
- out.writeEntityHeader(key, size);
+ if (DEBUG) Log.v(TAG, " ... key=" + keyEntry.key + " size=" + size);
+ out.writeEntityHeader(keyEntry.key, size);
out.writeEntityData(buf, size);
} finally {
in.close();
}
}
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
} catch (IOException e) {
Log.e(TAG, "Unable to read backup records", e);
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+ }
+
+ static class DecodedFilename implements Comparable<DecodedFilename> {
+ public File file;
+ public String key;
+
+ public DecodedFilename(File f) {
+ file = f;
+ key = new String(Base64.decode(f.getName()));
+ }
+
+ @Override
+ public int compareTo(DecodedFilename other) {
+ // sorts into ascending lexical order by decoded key
+ return key.compareTo(other.key);
+ }
+ }
+
+ // Return a list of the files in the given directory, sorted lexically by
+ // the Base64-decoded file name, not by the on-disk filename
+ private ArrayList<DecodedFilename> contentsByKey(File dir) {
+ File[] allFiles = dir.listFiles();
+ if (allFiles == null || allFiles.length == 0) {
+ return null;
+ }
+
+ // Decode the filenames into keys then sort lexically by key
+ ArrayList<DecodedFilename> contents = new ArrayList<DecodedFilename>();
+ for (File f : allFiles) {
+ contents.add(new DecodedFilename(f));
}
+ Collections.sort(contents);
+ return contents;
}
public void finishRestore() {
diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/core/java/com/android/internal/backup/LocalTransportService.java
index d05699a..77ac313 100644
--- a/core/java/com/android/internal/backup/LocalTransportService.java
+++ b/core/java/com/android/internal/backup/LocalTransportService.java
@@ -32,6 +32,6 @@ public class LocalTransportService extends Service {
@Override
public IBinder onBind(Intent intent) {
- return sTransport;
+ return sTransport.getBinder();
}
}
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 942995b..9df8ad5 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -22,7 +22,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
import com.android.internal.os.BackgroundThread;
@@ -243,7 +242,11 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
public boolean anyPackagesDisappearing() {
return mDisappearingPackages != null;
}
-
+
+ public boolean isReplacing() {
+ return mChangeType == PACKAGE_UPDATING;
+ }
+
public boolean isPackageModified(String packageName) {
if (mModifiedPackages != null) {
for (int i=mModifiedPackages.length-1; i>=0; i--) {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
new file mode 100644
index 0000000..7dbde69
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -0,0 +1,503 @@
+/*
+ * 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.internal.inputmethod;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.TreeMap;
+
+/**
+ * InputMethodSubtypeSwitchingController controls the switching behavior of the subtypes.
+ * <p>
+ * This class is designed to be used from and only from {@link InputMethodManagerService} by using
+ * {@link InputMethodManagerService#mMethodMap} as a global lock.
+ * </p>
+ */
+public class InputMethodSubtypeSwitchingController {
+ private static final String TAG = InputMethodSubtypeSwitchingController.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
+
+ public static class ImeSubtypeListItem implements Comparable<ImeSubtypeListItem> {
+ public final CharSequence mImeName;
+ public final CharSequence mSubtypeName;
+ public final InputMethodInfo mImi;
+ public final int mSubtypeId;
+ private final boolean mIsSystemLocale;
+ private final boolean mIsSystemLanguage;
+
+ public ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName,
+ InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale) {
+ mImeName = imeName;
+ mSubtypeName = subtypeName;
+ mImi = imi;
+ mSubtypeId = subtypeId;
+ if (TextUtils.isEmpty(subtypeLocale)) {
+ mIsSystemLocale = false;
+ mIsSystemLanguage = false;
+ } else {
+ mIsSystemLocale = subtypeLocale.equals(systemLocale);
+ mIsSystemLanguage = mIsSystemLocale
+ || subtypeLocale.startsWith(systemLocale.substring(0, 2));
+ }
+ }
+
+ @Override
+ public int compareTo(ImeSubtypeListItem other) {
+ if (TextUtils.isEmpty(mImeName)) {
+ return 1;
+ }
+ if (TextUtils.isEmpty(other.mImeName)) {
+ return -1;
+ }
+ if (!TextUtils.equals(mImeName, other.mImeName)) {
+ return mImeName.toString().compareTo(other.mImeName.toString());
+ }
+ if (TextUtils.equals(mSubtypeName, other.mSubtypeName)) {
+ return 0;
+ }
+ if (mIsSystemLocale) {
+ return -1;
+ }
+ if (other.mIsSystemLocale) {
+ return 1;
+ }
+ if (mIsSystemLanguage) {
+ return -1;
+ }
+ if (other.mIsSystemLanguage) {
+ return 1;
+ }
+ if (TextUtils.isEmpty(mSubtypeName)) {
+ return 1;
+ }
+ if (TextUtils.isEmpty(other.mSubtypeName)) {
+ return -1;
+ }
+ return mSubtypeName.toString().compareTo(other.mSubtypeName.toString());
+ }
+
+ @Override
+ public String toString() {
+ return "ImeSubtypeListItem{"
+ + "mImeName=" + mImeName
+ + " mSubtypeName=" + mSubtypeName
+ + " mSubtypeId=" + mSubtypeId
+ + " mIsSystemLocale=" + mIsSystemLocale
+ + " mIsSystemLanguage=" + mIsSystemLanguage
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof ImeSubtypeListItem) {
+ final ImeSubtypeListItem that = (ImeSubtypeListItem)o;
+ if (!Objects.equals(this.mImi, that.mImi)) {
+ return false;
+ }
+ if (this.mSubtypeId != that.mSubtypeId) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private static class InputMethodAndSubtypeList {
+ private final Context mContext;
+ // Used to load label
+ private final PackageManager mPm;
+ private final String mSystemLocaleStr;
+ private final InputMethodSettings mSettings;
+
+ public InputMethodAndSubtypeList(Context context, InputMethodSettings settings) {
+ mContext = context;
+ mSettings = settings;
+ mPm = context.getPackageManager();
+ final Locale locale = context.getResources().getConfiguration().locale;
+ mSystemLocaleStr = locale != null ? locale.toString() : "";
+ }
+
+ private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
+ new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
+ new Comparator<InputMethodInfo>() {
+ @Override
+ public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
+ if (imi2 == null)
+ return 0;
+ if (imi1 == null)
+ return 1;
+ if (mPm == null) {
+ return imi1.getId().compareTo(imi2.getId());
+ }
+ CharSequence imiId1 = imi1.loadLabel(mPm) + "/" + imi1.getId();
+ CharSequence imiId2 = imi2.loadLabel(mPm) + "/" + imi2.getId();
+ return imiId1.toString().compareTo(imiId2.toString());
+ }
+ });
+
+ public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
+ return getSortedInputMethodAndSubtypeList(true, false, false);
+ }
+
+ public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
+ boolean showSubtypes, boolean inputShown, boolean isScreenLocked) {
+ final ArrayList<ImeSubtypeListItem> imList =
+ new ArrayList<ImeSubtypeListItem>();
+ final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
+ mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
+ mContext);
+ if (immis == null || immis.size() == 0) {
+ return Collections.emptyList();
+ }
+ mSortedImmis.clear();
+ mSortedImmis.putAll(immis);
+ for (InputMethodInfo imi : mSortedImmis.keySet()) {
+ if (imi == null) {
+ continue;
+ }
+ List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
+ HashSet<String> enabledSubtypeSet = new HashSet<String>();
+ for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
+ enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
+ }
+ final CharSequence imeLabel = imi.loadLabel(mPm);
+ if (showSubtypes && enabledSubtypeSet.size() > 0) {
+ final int subtypeCount = imi.getSubtypeCount();
+ if (DEBUG) {
+ Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
+ }
+ for (int j = 0; j < subtypeCount; ++j) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+ final String subtypeHashCode = String.valueOf(subtype.hashCode());
+ // We show all enabled IMEs and subtypes when an IME is shown.
+ if (enabledSubtypeSet.contains(subtypeHashCode)
+ && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) {
+ final CharSequence subtypeLabel =
+ subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
+ .getDisplayName(mContext, imi.getPackageName(),
+ imi.getServiceInfo().applicationInfo);
+ imList.add(new ImeSubtypeListItem(imeLabel,
+ subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
+
+ // Removing this subtype from enabledSubtypeSet because we no
+ // longer need to add an entry of this subtype to imList to avoid
+ // duplicated entries.
+ enabledSubtypeSet.remove(subtypeHashCode);
+ }
+ }
+ } else {
+ imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null,
+ mSystemLocaleStr));
+ }
+ }
+ Collections.sort(imList);
+ return imList;
+ }
+ }
+
+ private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
+ return subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
+ subtype.hashCode()) : NOT_A_SUBTYPE_ID;
+ }
+
+ private static class StaticRotationList {
+ private final List<ImeSubtypeListItem> mImeSubtypeList;
+ public StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) {
+ mImeSubtypeList = imeSubtypeList;
+ }
+
+ /**
+ * Returns the index of the specified input method and subtype in the given list.
+ * @param imi The {@link InputMethodInfo} to be searched.
+ * @param subtype The {@link InputMethodSubtype} to be searched. null if the input method
+ * does not have a subtype.
+ * @return The index in the given list. -1 if not found.
+ */
+ private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) {
+ final int currentSubtypeId = calculateSubtypeId(imi, subtype);
+ final int N = mImeSubtypeList.size();
+ for (int i = 0; i < N; ++i) {
+ final ImeSubtypeListItem isli = mImeSubtypeList.get(i);
+ // Skip until the current IME/subtype is found.
+ if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
+ InputMethodInfo imi, InputMethodSubtype subtype) {
+ if (imi == null) {
+ return null;
+ }
+ if (mImeSubtypeList.size() <= 1) {
+ return null;
+ }
+ final int currentIndex = getIndex(imi, subtype);
+ if (currentIndex < 0) {
+ return null;
+ }
+ final int N = mImeSubtypeList.size();
+ for (int offset = 1; offset < N; ++offset) {
+ // Start searching the next IME/subtype from the next of the current index.
+ final int candidateIndex = (currentIndex + offset) % N;
+ final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex);
+ // Skip if searching inside the current IME only, but the candidate is not
+ // the current IME.
+ if (onlyCurrentIme && !imi.equals(candidate.mImi)) {
+ continue;
+ }
+ return candidate;
+ }
+ return null;
+ }
+ }
+
+ private static class DynamicRotationList {
+ private static final String TAG = DynamicRotationList.class.getSimpleName();
+ private final List<ImeSubtypeListItem> mImeSubtypeList;
+ private final int[] mUsageHistoryOfSubtypeListItemIndex;
+
+ private DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) {
+ mImeSubtypeList = imeSubtypeListItems;
+ mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()];
+ final int N = mImeSubtypeList.size();
+ for (int i = 0; i < N; i++) {
+ mUsageHistoryOfSubtypeListItemIndex[i] = i;
+ }
+ }
+
+ /**
+ * Returns the index of the specified object in
+ * {@link #mUsageHistoryOfSubtypeListItemIndex}.
+ * <p>We call the index of {@link #mUsageHistoryOfSubtypeListItemIndex} as "Usage Rank"
+ * so as not to be confused with the index in {@link #mImeSubtypeList}.
+ * @return -1 when the specified item doesn't belong to {@link #mImeSubtypeList} actually.
+ */
+ private int getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype) {
+ final int currentSubtypeId = calculateSubtypeId(imi, subtype);
+ final int N = mUsageHistoryOfSubtypeListItemIndex.length;
+ for (int usageRank = 0; usageRank < N; usageRank++) {
+ final int subtypeListItemIndex = mUsageHistoryOfSubtypeListItemIndex[usageRank];
+ final ImeSubtypeListItem subtypeListItem =
+ mImeSubtypeList.get(subtypeListItemIndex);
+ if (subtypeListItem.mImi.equals(imi) &&
+ subtypeListItem.mSubtypeId == currentSubtypeId) {
+ return usageRank;
+ }
+ }
+ // Not found in the known IME/Subtype list.
+ return -1;
+ }
+
+ public void onUserAction(InputMethodInfo imi, InputMethodSubtype subtype) {
+ final int currentUsageRank = getUsageRank(imi, subtype);
+ // Do nothing if currentUsageRank == -1 (not found), or currentUsageRank == 0
+ if (currentUsageRank <= 0) {
+ return;
+ }
+ final int currentItemIndex = mUsageHistoryOfSubtypeListItemIndex[currentUsageRank];
+ System.arraycopy(mUsageHistoryOfSubtypeListItemIndex, 0,
+ mUsageHistoryOfSubtypeListItemIndex, 1, currentUsageRank);
+ mUsageHistoryOfSubtypeListItemIndex[0] = currentItemIndex;
+ }
+
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
+ InputMethodInfo imi, InputMethodSubtype subtype) {
+ int currentUsageRank = getUsageRank(imi, subtype);
+ if (currentUsageRank < 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "IME/subtype is not found: " + imi.getId() + ", " + subtype);
+ }
+ return null;
+ }
+ final int N = mUsageHistoryOfSubtypeListItemIndex.length;
+ for (int i = 1; i < N; i++) {
+ final int subtypeListItemRank = (currentUsageRank + i) % N;
+ final int subtypeListItemIndex =
+ mUsageHistoryOfSubtypeListItemIndex[subtypeListItemRank];
+ final ImeSubtypeListItem subtypeListItem =
+ mImeSubtypeList.get(subtypeListItemIndex);
+ if (onlyCurrentIme && !imi.equals(subtypeListItem.mImi)) {
+ continue;
+ }
+ return subtypeListItem;
+ }
+ return null;
+ }
+ }
+
+ @VisibleForTesting
+ public static class ControllerImpl {
+ private final DynamicRotationList mSwitchingAwareRotationList;
+ private final StaticRotationList mSwitchingUnawareRotationList;
+
+ public static ControllerImpl createFrom(final ControllerImpl currentInstance,
+ final List<ImeSubtypeListItem> sortedEnabledItems) {
+ DynamicRotationList switchingAwareRotationList = null;
+ {
+ final List<ImeSubtypeListItem> switchingAwareImeSubtypes =
+ filterImeSubtypeList(sortedEnabledItems,
+ true /* supportsSwitchingToNextInputMethod */);
+ if (currentInstance != null &&
+ currentInstance.mSwitchingAwareRotationList != null &&
+ Objects.equals(currentInstance.mSwitchingAwareRotationList.mImeSubtypeList,
+ switchingAwareImeSubtypes)) {
+ // Can reuse the current instance.
+ switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList;
+ }
+ if (switchingAwareRotationList == null) {
+ switchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes);
+ }
+ }
+
+ StaticRotationList switchingUnawareRotationList = null;
+ {
+ final List<ImeSubtypeListItem> switchingUnawareImeSubtypes = filterImeSubtypeList(
+ sortedEnabledItems, false /* supportsSwitchingToNextInputMethod */);
+ if (currentInstance != null &&
+ currentInstance.mSwitchingUnawareRotationList != null &&
+ Objects.equals(
+ currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList,
+ switchingUnawareImeSubtypes)) {
+ // Can reuse the current instance.
+ switchingUnawareRotationList = currentInstance.mSwitchingUnawareRotationList;
+ }
+ if (switchingUnawareRotationList == null) {
+ switchingUnawareRotationList =
+ new StaticRotationList(switchingUnawareImeSubtypes);
+ }
+ }
+
+ return new ControllerImpl(switchingAwareRotationList, switchingUnawareRotationList);
+ }
+
+ private ControllerImpl(final DynamicRotationList switchingAwareRotationList,
+ final StaticRotationList switchingUnawareRotationList) {
+ mSwitchingAwareRotationList = switchingAwareRotationList;
+ mSwitchingUnawareRotationList = switchingUnawareRotationList;
+ }
+
+ public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
+ InputMethodSubtype subtype) {
+ if (imi == null) {
+ return null;
+ }
+ if (imi.supportsSwitchingToNextInputMethod()) {
+ return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ subtype);
+ } else {
+ return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ subtype);
+ }
+ }
+
+ public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
+ if (imi == null) {
+ return;
+ }
+ if (imi.supportsSwitchingToNextInputMethod()) {
+ mSwitchingAwareRotationList.onUserAction(imi, subtype);
+ }
+ }
+
+ private static List<ImeSubtypeListItem> filterImeSubtypeList(
+ final List<ImeSubtypeListItem> items,
+ final boolean supportsSwitchingToNextInputMethod) {
+ final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
+ final int ALL_ITEMS_COUNT = items.size();
+ for (int i = 0; i < ALL_ITEMS_COUNT; i++) {
+ final ImeSubtypeListItem item = items.get(i);
+ if (item.mImi.supportsSwitchingToNextInputMethod() ==
+ supportsSwitchingToNextInputMethod) {
+ result.add(item);
+ }
+ }
+ return result;
+ }
+ }
+
+ private final InputMethodSettings mSettings;
+ private InputMethodAndSubtypeList mSubtypeList;
+ private ControllerImpl mController;
+
+ private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
+ mSettings = settings;
+ resetCircularListLocked(context);
+ }
+
+ public static InputMethodSubtypeSwitchingController createInstanceLocked(
+ InputMethodSettings settings, Context context) {
+ return new InputMethodSubtypeSwitchingController(settings, context);
+ }
+
+ public void onCommitTextLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
+ if (mController == null) {
+ if (DEBUG) {
+ Log.e(TAG, "mController shouldn't be null.");
+ }
+ return;
+ }
+ mController.onUserActionLocked(imi, subtype);
+ }
+
+ public void resetCircularListLocked(Context context) {
+ mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
+ mController = ControllerImpl.createFrom(mController,
+ mSubtypeList.getSortedInputMethodAndSubtypeList());
+ }
+
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
+ InputMethodSubtype subtype) {
+ if (mController == null) {
+ if (DEBUG) {
+ Log.e(TAG, "mController shouldn't be null.");
+ }
+ return null;
+ }
+ return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
+ }
+
+ public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes,
+ boolean inputShown, boolean isScreenLocked) {
+ return mSubtypeList.getSortedInputMethodAndSubtypeList(
+ showSubtypes, inputShown, isScreenLocked);
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 63d018f..ac3274d 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -504,6 +504,7 @@ public class InputMethodUtils {
private String mEnabledInputMethodsStrCache;
private int mCurrentUserId;
+ private int[] mCurrentProfileIds = new int[0];
private static void buildEnabledInputMethodsSettingString(
StringBuilder builder, Pair<String, ArrayList<String>> pair) {
@@ -536,6 +537,22 @@ public class InputMethodUtils {
mCurrentUserId = userId;
}
+ public void setCurrentProfileIds(int[] currentProfileIds) {
+ synchronized (this) {
+ mCurrentProfileIds = currentProfileIds;
+ }
+ }
+
+ public boolean isCurrentProfile(int userId) {
+ synchronized (this) {
+ if (userId == mCurrentUserId) return true;
+ for (int i = 0; i < mCurrentProfileIds.length; i++) {
+ if (userId == mCurrentProfileIds[i]) return true;
+ }
+ return false;
+ }
+ }
+
public List<InputMethodInfo> getEnabledInputMethodListLocked() {
return createEnabledInputMethodListLocked(
getEnabledInputMethodsAndSubtypeListLocked());
@@ -959,5 +976,16 @@ public class InputMethodUtils {
addSubtypeToHistory(curMethodId, subtypeId);
}
}
+
+ public HashMap<InputMethodInfo, List<InputMethodSubtype>>
+ getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(Context context) {
+ HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes =
+ new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
+ for (InputMethodInfo imi: getEnabledInputMethodListLocked()) {
+ enabledInputMethodAndSubtypes.put(
+ imi, getEnabledInputMethodSubtypeListLocked(context, imi, true));
+ }
+ return enabledInputMethodAndSubtypes;
+ }
}
}
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 8282d23..e2a2b1e 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -17,6 +17,7 @@
package com.android.internal.net;
import static android.net.NetworkStats.SET_ALL;
+import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
@@ -26,6 +27,7 @@ import android.os.StrictMode;
import android.os.SystemClock;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
import java.io.File;
@@ -165,22 +167,32 @@ public class NetworkStatsFactory {
}
public NetworkStats readNetworkStatsDetail() throws IOException {
- return readNetworkStatsDetail(UID_ALL);
+ return readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);
}
- public NetworkStats readNetworkStatsDetail(int limitUid) throws IOException {
+ public NetworkStats readNetworkStatsDetail(int limitUid, String[] limitIfaces, int limitTag,
+ NetworkStats lastStats)
+ throws IOException {
if (USE_NATIVE_PARSING) {
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid) != 0) {
+ final NetworkStats stats;
+ if (lastStats != null) {
+ stats = lastStats;
+ stats.setElapsedRealtime(SystemClock.elapsedRealtime());
+ } else {
+ stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
+ }
+ if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
+ limitIfaces, limitTag) != 0) {
throw new IOException("Failed to parse network stats");
}
if (SANITY_CHECK_NATIVE) {
- final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
+ final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid,
+ limitIfaces, limitTag);
assertEquals(javaStats, stats);
}
return stats;
} else {
- return javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
+ return javaReadNetworkStatsDetail(mStatsXtUid, limitUid, limitIfaces, limitTag);
}
}
@@ -189,7 +201,8 @@ public class NetworkStatsFactory {
* expected to monotonically increase since device boot.
*/
@VisibleForTesting
- public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid)
+ public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid,
+ String[] limitIfaces, int limitTag)
throws IOException {
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
@@ -222,7 +235,9 @@ public class NetworkStatsFactory {
entry.txBytes = reader.nextLong();
entry.txPackets = reader.nextLong();
- if (limitUid == UID_ALL || limitUid == entry.uid) {
+ if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
+ && (limitUid == UID_ALL || limitUid == entry.uid)
+ && (limitTag == TAG_ALL || limitTag == entry.tag)) {
stats.addValues(entry);
}
@@ -264,5 +279,5 @@ public class NetworkStatsFactory {
*/
@VisibleForTesting
public static native int nativeReadNetworkStatsDetail(
- NetworkStats stats, String path, int limitUid);
+ NetworkStats stats, String path, int limitUid, String[] limitIfaces, int limitTag);
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 98599d0..0d00f41 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -25,8 +25,6 @@ import android.os.UserHandle;
import android.net.RouteInfo;
import android.net.LinkAddress;
-import com.android.internal.util.Preconditions;
-
import java.net.InetAddress;
import java.util.List;
import java.util.ArrayList;
diff --git a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java b/core/java/com/android/internal/notification/DemoContactNotificationScorer.java
deleted file mode 100644
index f484724..0000000
--- a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
-* 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.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.Settings;
-import android.text.SpannableString;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This NotificationScorer bumps up the priority of notifications that contain references to the
- * display names of starred contacts. The references it picks up are spannable strings which, in
- * their entirety, match the display name of some starred contact. The magnitude of the bump ranges
- * from 0 to 15 (assuming NOTIFICATION_PRIORITY_MULTIPLIER = 10) depending on the initial score, and
- * the mapping is defined by priorityBumpMap. In a production version of this scorer, a notification
- * extra will be used to specify contact identifiers.
- */
-
-public class DemoContactNotificationScorer implements NotificationScorer {
- private static final String TAG = "DemoContactNotificationScorer";
- private static final boolean DBG = false;
-
- protected static final boolean ENABLE_CONTACT_SCORER = true;
- private static final String SETTING_ENABLE_SCORER = "contact_scorer_enabled";
- protected boolean mEnabled;
-
- // see NotificationManagerService
- private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
-
- private Context mContext;
-
- private static final List<String> RELEVANT_KEYS_LIST = Arrays.asList(
- Notification.EXTRA_INFO_TEXT, Notification.EXTRA_TEXT, Notification.EXTRA_TEXT_LINES,
- Notification.EXTRA_SUB_TEXT, Notification.EXTRA_TITLE
- );
-
- private static final String[] PROJECTION = new String[] {
- ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME
- };
-
- private static final Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI;
-
- private static List<String> extractSpannedStrings(CharSequence charSequence) {
- if (charSequence == null) return Collections.emptyList();
- if (!(charSequence instanceof SpannableString)) {
- return Arrays.asList(charSequence.toString());
- }
- SpannableString spannableString = (SpannableString)charSequence;
- // get all spans
- Object[] ssArr = spannableString.getSpans(0, spannableString.length(), Object.class);
- // spanned string sequences
- ArrayList<String> sss = new ArrayList<String>();
- for (Object spanObj : ssArr) {
- try {
- sss.add(spannableString.subSequence(spannableString.getSpanStart(spanObj),
- spannableString.getSpanEnd(spanObj)).toString());
- } catch(StringIndexOutOfBoundsException e) {
- Slog.e(TAG, "Bad indices when extracting spanned subsequence", e);
- }
- }
- return sss;
- };
-
- private static String getQuestionMarksInParens(int n) {
- StringBuilder sb = new StringBuilder("(");
- for (int i = 0; i < n; i++) {
- if (sb.length() > 1) sb.append(',');
- sb.append('?');
- }
- sb.append(")");
- return sb.toString();
- }
-
- private boolean hasStarredContact(Bundle extras) {
- if (extras == null) return false;
- ArrayList<String> qStrings = new ArrayList<String>();
- // build list to query against the database for display names.
- for (String rk: RELEVANT_KEYS_LIST) {
- if (extras.get(rk) == null) {
- continue;
- } else if (extras.get(rk) instanceof CharSequence) {
- qStrings.addAll(extractSpannedStrings((CharSequence) extras.get(rk)));
- } else if (extras.get(rk) instanceof CharSequence[]) {
- // this is intended for Notification.EXTRA_TEXT_LINES
- for (CharSequence line: (CharSequence[]) extras.get(rk)){
- qStrings.addAll(extractSpannedStrings(line));
- }
- } else {
- Slog.w(TAG, "Strange, the extra " + rk + " is of unexpected type.");
- }
- }
- if (qStrings.isEmpty()) return false;
- String[] qStringsArr = qStrings.toArray(new String[qStrings.size()]);
-
- String selection = ContactsContract.Contacts.DISPLAY_NAME + " IN "
- + getQuestionMarksInParens(qStringsArr.length) + " AND "
- + ContactsContract.Contacts.STARRED+" ='1'";
-
- Cursor c = null;
- try {
- c = mContext.getContentResolver().query(
- CONTACTS_URI, PROJECTION, selection, qStringsArr, null);
- if (c != null) return c.getCount() > 0;
- } catch(Throwable t) {
- Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return false;
- }
-
- private final static int clamp(int x, int low, int high) {
- return (x < low) ? low : ((x > high) ? high : x);
- }
-
- private static int priorityBumpMap(int incomingScore) {
- //assumption is that scale runs from [-2*pm, 2*pm]
- int pm = NOTIFICATION_PRIORITY_MULTIPLIER;
- int theScore = incomingScore;
- // enforce input in range
- theScore = clamp(theScore, -2 * pm, 2 * pm);
- if (theScore != incomingScore) return incomingScore;
- // map -20 -> -20 and -10 -> 5 (when pm = 10)
- if (theScore <= -pm) {
- theScore += 1.5 * (theScore + 2 * pm);
- } else {
- // map 0 -> 10, 10 -> 15, 20 -> 20;
- theScore += 0.5 * (2 * pm - theScore);
- }
- if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore
- + ", score after " + theScore + ".");
- return theScore;
- }
-
- @Override
- public void initialize(Context context) {
- if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + ".");
- mContext = context;
- mEnabled = ENABLE_CONTACT_SCORER && 1 == Settings.Global.getInt(
- mContext.getContentResolver(), SETTING_ENABLE_SCORER, 0);
- }
-
- @Override
- public int getScore(Notification notification, int score) {
- if (notification == null || !mEnabled) {
- if (DBG) Slog.w(TAG, "empty notification? scorer disabled?");
- return score;
- }
- boolean hasStarredPriority = hasStarredContact(notification.extras);
-
- if (DBG) {
- if (hasStarredPriority) {
- Slog.v(TAG, "Notification references starred contact. Promoted!");
- } else {
- Slog.v(TAG, "Notification lacks any starred contact reference. Not promoted!");
- }
- }
- if (hasStarredPriority) score = priorityBumpMap(score);
- return score;
- }
-}
-
diff --git a/core/java/com/android/internal/notification/NotificationScorer.java b/core/java/com/android/internal/notification/NotificationScorer.java
deleted file mode 100644
index 863c08c..0000000
--- a/core/java/com/android/internal/notification/NotificationScorer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-* 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.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-
-public interface NotificationScorer {
-
- public void initialize(Context context);
- public int getScore(Notification notification, int score);
-
-}
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
new file mode 100644
index 0000000..6ca24d7
--- /dev/null
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os;
+
+import android.os.BatteryStats.Uid;
+
+/**
+ * Contains power usage of an application, system service, or hardware type.
+ */
+public class BatterySipper implements Comparable<BatterySipper> {
+ public int userId;
+ public Uid uidObj;
+ public double value;
+ public double[] values;
+ public DrainType drainType;
+ public long usageTime;
+ public long cpuTime;
+ public long gpsTime;
+ public long wifiRunningTime;
+ public long cpuFgTime;
+ public long wakeLockTime;
+ public long mobileRxPackets;
+ public long mobileTxPackets;
+ public long mobileActive;
+ public int mobileActiveCount;
+ public double mobilemspp; // milliseconds per packet
+ public long wifiRxPackets;
+ public long wifiTxPackets;
+ public long mobileRxBytes;
+ public long mobileTxBytes;
+ public long wifiRxBytes;
+ public long wifiTxBytes;
+ public double percent;
+ public double noCoveragePercent;
+ public String[] mPackages;
+ public String packageWithHighestDrain;
+
+ public enum DrainType {
+ IDLE,
+ CELL,
+ PHONE,
+ WIFI,
+ BLUETOOTH,
+ SCREEN,
+ APP,
+ USER,
+ UNACCOUNTED,
+ OVERCOUNTED
+ }
+
+ public BatterySipper(DrainType drainType, Uid uid, double[] values) {
+ this.values = values;
+ if (values != null) value = values[0];
+ this.drainType = drainType;
+ uidObj = uid;
+ }
+
+ public double[] getValues() {
+ return values;
+ }
+
+ public void computeMobilemspp() {
+ long packets = mobileRxPackets+mobileTxPackets;
+ mobilemspp = packets > 0 ? (mobileActive / (double)packets) : 0;
+ }
+
+ @Override
+ public int compareTo(BatterySipper other) {
+ // Return the flipped value because we want the items in descending order
+ return Double.compare(other.value, value);
+ }
+
+ /**
+ * Gets a list of packages associated with the current user
+ */
+ public String[] getPackages() {
+ return mPackages;
+ }
+
+ public int getUid() {
+ // Bail out if the current sipper is not an App sipper.
+ if (uidObj == null) {
+ return 0;
+ }
+ return uidObj.getUid();
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
new file mode 100644
index 0000000..7ff949e
--- /dev/null
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -0,0 +1,838 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os;
+
+import static android.os.BatteryStats.NETWORK_MOBILE_RX_DATA;
+import static android.os.BatteryStats.NETWORK_MOBILE_TX_DATA;
+import static android.os.BatteryStats.NETWORK_WIFI_RX_DATA;
+import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.net.ConnectivityManager;
+import android.os.BatteryStats;
+import android.os.BatteryStats.Uid;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.telephony.SignalStrength;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.BatterySipper.DrainType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A helper class for retrieving the power usage information for all applications and services.
+ *
+ * The caller must initialize this class as soon as activity object is ready to use (for example, in
+ * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
+ */
+public class BatteryStatsHelper {
+
+ private static final boolean DEBUG = false;
+
+ private static final String TAG = BatteryStatsHelper.class.getSimpleName();
+
+ private static BatteryStats sStatsXfer;
+ private static Intent sBatteryBroadcastXfer;
+
+ final private Context mContext;
+ final private boolean mCollectBatteryBroadcast;
+
+ private IBatteryStats mBatteryInfo;
+ private BatteryStats mStats;
+ private Intent mBatteryBroadcast;
+ private PowerProfile mPowerProfile;
+
+ private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
+ private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>();
+ private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>();
+ private final SparseArray<List<BatterySipper>> mUserSippers
+ = new SparseArray<List<BatterySipper>>();
+ private final SparseArray<Double> mUserPower = new SparseArray<Double>();
+
+ private final List<BatterySipper> mMobilemsppList = new ArrayList<BatterySipper>();
+
+ private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
+ private int mAsUser = 0;
+
+ long mRawRealtime;
+ long mRawUptime;
+ long mBatteryRealtime;
+ long mBatteryUptime;
+ long mTypeBatteryRealtime;
+ long mTypeBatteryUptime;
+ long mBatteryTimeRemaining;
+ long mChargeTimeRemaining;
+
+ private long mStatsPeriod = 0;
+ private double mMaxPower = 1;
+ private double mComputedPower;
+ private double mTotalPower;
+ private double mWifiPower;
+ private double mBluetoothPower;
+ private double mMinDrainedPower;
+ private double mMaxDrainedPower;
+
+ // How much the apps together have kept the mobile radio active.
+ private long mAppMobileActive;
+
+ // How much the apps together have left WIFI running.
+ private long mAppWifiRunning;
+
+ public BatteryStatsHelper(Context context) {
+ this(context, true);
+ }
+
+ public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
+ mContext = context;
+ mCollectBatteryBroadcast = collectBatteryBroadcast;
+ }
+
+ /** Clears the current stats and forces recreating for future use. */
+ public void clearStats() {
+ mStats = null;
+ }
+
+ public BatteryStats getStats() {
+ if (mStats == null) {
+ load();
+ }
+ return mStats;
+ }
+
+ public Intent getBatteryBroadcast() {
+ if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
+ load();
+ }
+ return mBatteryBroadcast;
+ }
+
+ public PowerProfile getPowerProfile() {
+ return mPowerProfile;
+ }
+
+ public void create(BatteryStats stats) {
+ mPowerProfile = new PowerProfile(mContext);
+ mStats = stats;
+ }
+
+ public void create(Bundle icicle) {
+ if (icicle != null) {
+ mStats = sStatsXfer;
+ mBatteryBroadcast = sBatteryBroadcastXfer;
+ }
+ mBatteryInfo = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+ mPowerProfile = new PowerProfile(mContext);
+ }
+
+ public void storeState() {
+ sStatsXfer = mStats;
+ sBatteryBroadcastXfer = mBatteryBroadcast;
+ }
+
+ public static String makemAh(double power) {
+ if (power < .00001) return String.format("%.8f", power);
+ else if (power < .0001) return String.format("%.7f", power);
+ else if (power < .001) return String.format("%.6f", power);
+ else if (power < .01) return String.format("%.5f", power);
+ else if (power < .1) return String.format("%.4f", power);
+ else if (power < 1) return String.format("%.3f", power);
+ else if (power < 10) return String.format("%.2f", power);
+ else if (power < 100) return String.format("%.1f", power);
+ else return String.format("%.0f", power);
+ }
+
+ /**
+ * Refreshes the power usage list.
+ */
+ public void refreshStats(int statsType, int asUser) {
+ refreshStats(statsType, asUser, SystemClock.elapsedRealtime() * 1000,
+ SystemClock.uptimeMillis() * 1000);
+ }
+
+ public void refreshStats(int statsType, int asUser, long rawRealtimeUs, long rawUptimeUs) {
+ // Initialize mStats if necessary.
+ getStats();
+
+ mMaxPower = 0;
+ mComputedPower = 0;
+ mTotalPower = 0;
+ mWifiPower = 0;
+ mBluetoothPower = 0;
+ mAppMobileActive = 0;
+ mAppWifiRunning = 0;
+
+ mUsageList.clear();
+ mWifiSippers.clear();
+ mBluetoothSippers.clear();
+ mUserSippers.clear();
+ mUserPower.clear();
+ mMobilemsppList.clear();
+
+ if (mStats == null) {
+ return;
+ }
+
+ mStatsType = statsType;
+ mAsUser = asUser;
+ mRawUptime = rawUptimeUs;
+ mRawRealtime = rawRealtimeUs;
+ mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs);
+ mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs);
+ mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);
+ mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
+ mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
+ mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);
+
+ if (DEBUG) {
+ Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime="
+ + (rawUptimeUs/1000));
+ Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime="
+ + (mBatteryUptime/1000));
+ Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime="
+ + (mTypeBatteryUptime/1000));
+ }
+ mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
+ * mPowerProfile.getBatteryCapacity()) / 100;
+ mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
+ * mPowerProfile.getBatteryCapacity()) / 100;
+
+ processAppUsage();
+
+ // Before aggregating apps in to users, collect all apps to sort by their ms per packet.
+ for (int i=0; i<mUsageList.size(); i++) {
+ BatterySipper bs = mUsageList.get(i);
+ bs.computeMobilemspp();
+ if (bs.mobilemspp != 0) {
+ mMobilemsppList.add(bs);
+ }
+ }
+ for (int i=0; i<mUserSippers.size(); i++) {
+ List<BatterySipper> user = mUserSippers.valueAt(i);
+ for (int j=0; j<user.size(); j++) {
+ BatterySipper bs = user.get(j);
+ bs.computeMobilemspp();
+ if (bs.mobilemspp != 0) {
+ mMobilemsppList.add(bs);
+ }
+ }
+ }
+ Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
+ @Override
+ public int compare(BatterySipper lhs, BatterySipper rhs) {
+ if (lhs.mobilemspp < rhs.mobilemspp) {
+ return 1;
+ } else if (lhs.mobilemspp > rhs.mobilemspp) {
+ return -1;
+ }
+ return 0;
+ }
+ });
+
+ processMiscUsage();
+
+ if (DEBUG) {
+ Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
+ + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
+ }
+ mTotalPower = mComputedPower;
+ if (mStats.getLowDischargeAmountSinceCharge() > 1) {
+ if (mMinDrainedPower > mComputedPower) {
+ double amount = mMinDrainedPower - mComputedPower;
+ mTotalPower = mMinDrainedPower;
+ addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);
+ } else if (mMaxDrainedPower < mComputedPower) {
+ double amount = mComputedPower - mMaxDrainedPower;
+ addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);
+ }
+ }
+
+ Collections.sort(mUsageList);
+ }
+
+ private void processAppUsage() {
+ SensorManager sensorManager = (SensorManager) mContext.getSystemService(
+ Context.SENSOR_SERVICE);
+ final int which = mStatsType;
+ final int speedSteps = mPowerProfile.getNumSpeedSteps();
+ final double[] powerCpuNormal = new double[speedSteps];
+ final long[] cpuSpeedStepTimes = new long[speedSteps];
+ for (int p = 0; p < speedSteps; p++) {
+ powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
+ }
+ final double mobilePowerPerPacket = getMobilePowerPerPacket();
+ final double mobilePowerPerMs = getMobilePowerPerMs();
+ final double wifiPowerPerPacket = getWifiPowerPerPacket();
+ long appWakelockTimeUs = 0;
+ BatterySipper osApp = null;
+ mStatsPeriod = mTypeBatteryRealtime;
+ SparseArray<? extends Uid> uidStats = mStats.getUidStats();
+ final int NU = uidStats.size();
+ for (int iu = 0; iu < NU; iu++) {
+ Uid u = uidStats.valueAt(iu);
+ double p; // in mAs
+ double power = 0; // in mAs
+ double highestDrain = 0;
+ String packageWithHighestDrain = null;
+ Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
+ long cpuTime = 0;
+ long cpuFgTime = 0;
+ long wakelockTime = 0;
+ long gpsTime = 0;
+ if (processStats.size() > 0) {
+ // Process CPU time
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
+ : processStats.entrySet()) {
+ Uid.Proc ps = ent.getValue();
+ final long userTime = ps.getUserTime(which);
+ final long systemTime = ps.getSystemTime(which);
+ final long foregroundTime = ps.getForegroundTime(which);
+ cpuFgTime += foregroundTime * 10; // convert to millis
+ final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis
+ int totalTimeAtSpeeds = 0;
+ // Get the total first
+ for (int step = 0; step < speedSteps; step++) {
+ cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which);
+ totalTimeAtSpeeds += cpuSpeedStepTimes[step];
+ }
+ if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1;
+ // Then compute the ratio of time spent at each speed
+ double processPower = 0;
+ for (int step = 0; step < speedSteps; step++) {
+ double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds;
+ if (DEBUG && ratio != 0) Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
+ + step + " ratio=" + makemAh(ratio) + " power="
+ + makemAh(ratio*tmpCpuTime*powerCpuNormal[step] / (60*60*1000)));
+ processPower += ratio * tmpCpuTime * powerCpuNormal[step];
+ }
+ cpuTime += tmpCpuTime;
+ if (DEBUG && processPower != 0) {
+ Log.d(TAG, String.format("process %s, cpu power=%s",
+ ent.getKey(), makemAh(processPower / (60*60*1000))));
+ }
+ power += processPower;
+ if (packageWithHighestDrain == null
+ || packageWithHighestDrain.startsWith("*")) {
+ highestDrain = processPower;
+ packageWithHighestDrain = ent.getKey();
+ } else if (highestDrain < processPower
+ && !ent.getKey().startsWith("*")) {
+ highestDrain = processPower;
+ packageWithHighestDrain = ent.getKey();
+ }
+ }
+ }
+ if (cpuFgTime > cpuTime) {
+ if (DEBUG && cpuFgTime > cpuTime + 10000) {
+ Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
+ }
+ cpuTime = cpuFgTime; // Statistics may not have been gathered yet.
+ }
+ power /= (60*60*1000);
+
+ // Process wake lock usage
+ Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats();
+ for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry
+ : wakelockStats.entrySet()) {
+ Uid.Wakelock wakelock = wakelockEntry.getValue();
+ // Only care about partial wake locks since full wake locks
+ // are canceled when the user turns the screen off.
+ BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
+ if (timer != null) {
+ wakelockTime += timer.getTotalTimeLocked(mRawRealtime, which);
+ }
+ }
+ appWakelockTimeUs += wakelockTime;
+ wakelockTime /= 1000; // convert to millis
+
+ // Add cost of holding a wake lock
+ p = (wakelockTime
+ * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60*60*1000);
+ if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wake "
+ + wakelockTime + " power=" + makemAh(p));
+ power += p;
+
+ // Add cost of mobile traffic
+ final long mobileRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
+ final long mobileTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
+ final long mobileRxB = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, mStatsType);
+ final long mobileTxB = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, mStatsType);
+ final long mobileActive = u.getMobileRadioActiveTime(mStatsType);
+ if (mobileActive > 0) {
+ // We are tracking when the radio is up, so can use the active time to
+ // determine power use.
+ mAppMobileActive += mobileActive;
+ p = (mobilePowerPerMs * mobileActive) / 1000;
+ } else {
+ // We are not tracking when the radio is up, so must approximate power use
+ // based on the number of packets.
+ p = (mobileRx + mobileTx) * mobilePowerPerPacket;
+ }
+ if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
+ + (mobileRx+mobileTx) + " active time " + mobileActive
+ + " power=" + makemAh(p));
+ power += p;
+
+ // Add cost of wifi traffic
+ final long wifiRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType);
+ final long wifiTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType);
+ final long wifiRxB = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType);
+ final long wifiTxB = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType);
+ p = (wifiRx + wifiTx) * wifiPowerPerPacket;
+ if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi packets "
+ + (mobileRx+mobileTx) + " power=" + makemAh(p));
+ power += p;
+
+ // Add cost of keeping WIFI running.
+ long wifiRunningTimeMs = u.getWifiRunningTime(mRawRealtime, which) / 1000;
+ mAppWifiRunning += wifiRunningTimeMs;
+ p = (wifiRunningTimeMs
+ * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000);
+ if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi running "
+ + wifiRunningTimeMs + " power=" + makemAh(p));
+ power += p;
+
+ // Add cost of WIFI scans
+ long wifiScanTimeMs = u.getWifiScanTime(mRawRealtime, which) / 1000;
+ p = (wifiScanTimeMs
+ * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / (60*60*1000);
+ if (DEBUG) Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs
+ + " power=" + makemAh(p));
+ power += p;
+ for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
+ long batchScanTimeMs = u.getWifiBatchedScanTime(bin, mRawRealtime, which) / 1000;
+ p = ((batchScanTimeMs
+ * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin))
+ ) / (60*60*1000);
+ if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin
+ + " time=" + batchScanTimeMs + " power=" + makemAh(p));
+ power += p;
+ }
+
+ // Process Sensor usage
+ Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
+ for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry
+ : sensorStats.entrySet()) {
+ Uid.Sensor sensor = sensorEntry.getValue();
+ int sensorHandle = sensor.getHandle();
+ BatteryStats.Timer timer = sensor.getSensorTime();
+ long sensorTime = timer.getTotalTimeLocked(mRawRealtime, which) / 1000;
+ double multiplier = 0;
+ switch (sensorHandle) {
+ case Uid.Sensor.GPS:
+ multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON);
+ gpsTime = sensorTime;
+ break;
+ default:
+ List<Sensor> sensorList = sensorManager.getSensorList(
+ android.hardware.Sensor.TYPE_ALL);
+ for (android.hardware.Sensor s : sensorList) {
+ if (s.getHandle() == sensorHandle) {
+ multiplier = s.getPower();
+ break;
+ }
+ }
+ }
+ p = (multiplier * sensorTime) / (60*60*1000);
+ if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle
+ + " time=" + sensorTime + " power=" + makemAh(p));
+ power += p;
+ }
+
+ if (DEBUG && power != 0) Log.d(TAG, String.format("UID %d: total power=%s",
+ u.getUid(), makemAh(power)));
+
+ // Add the app to the list if it is consuming power
+ final int userId = UserHandle.getUserId(u.getUid());
+ if (power != 0 || u.getUid() == 0) {
+ BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u,
+ new double[] {power});
+ app.cpuTime = cpuTime;
+ app.gpsTime = gpsTime;
+ app.wifiRunningTime = wifiRunningTimeMs;
+ app.cpuFgTime = cpuFgTime;
+ app.wakeLockTime = wakelockTime;
+ app.mobileRxPackets = mobileRx;
+ app.mobileTxPackets = mobileTx;
+ app.mobileActive = mobileActive / 1000;
+ app.mobileActiveCount = u.getMobileRadioActiveCount(mStatsType);
+ app.wifiRxPackets = wifiRx;
+ app.wifiTxPackets = wifiTx;
+ app.mobileRxBytes = mobileRxB;
+ app.mobileTxBytes = mobileTxB;
+ app.wifiRxBytes = wifiRxB;
+ app.wifiTxBytes = wifiTxB;
+ app.packageWithHighestDrain = packageWithHighestDrain;
+ if (u.getUid() == Process.WIFI_UID) {
+ mWifiSippers.add(app);
+ mWifiPower += power;
+ } else if (u.getUid() == Process.BLUETOOTH_UID) {
+ mBluetoothSippers.add(app);
+ mBluetoothPower += power;
+ } else if (mAsUser != UserHandle.USER_ALL && userId != mAsUser
+ && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) {
+ List<BatterySipper> list = mUserSippers.get(userId);
+ if (list == null) {
+ list = new ArrayList<BatterySipper>();
+ mUserSippers.put(userId, list);
+ }
+ list.add(app);
+ if (power != 0) {
+ Double userPower = mUserPower.get(userId);
+ if (userPower == null) {
+ userPower = power;
+ } else {
+ userPower += power;
+ }
+ mUserPower.put(userId, userPower);
+ }
+ } else {
+ mUsageList.add(app);
+ if (power > mMaxPower) mMaxPower = power;
+ mComputedPower += power;
+ }
+ if (u.getUid() == 0) {
+ osApp = app;
+ }
+ }
+ }
+
+ // The device has probably been awake for longer than the screen on
+ // time and application wake lock time would account for. Assign
+ // this remainder to the OS, if possible.
+ if (osApp != null) {
+ long wakeTimeMillis = mBatteryUptime / 1000;
+ wakeTimeMillis -= (appWakelockTimeUs / 1000)
+ + (mStats.getScreenOnTime(mRawRealtime, which) / 1000);
+ if (wakeTimeMillis > 0) {
+ double power = (wakeTimeMillis
+ * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE))
+ / (60*60*1000);
+ if (DEBUG) Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
+ + makemAh(power));
+ osApp.wakeLockTime += wakeTimeMillis;
+ osApp.value += power;
+ osApp.values[0] += power;
+ if (osApp.value > mMaxPower) mMaxPower = osApp.value;
+ mComputedPower += power;
+ }
+ }
+ }
+
+ private void addPhoneUsage() {
+ long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000;
+ double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
+ * phoneOnTimeMs / (60*60*1000);
+ if (phoneOnPower != 0) {
+ BatterySipper bs = addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
+ }
+ }
+
+ private void addScreenUsage() {
+ double power = 0;
+ long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000;
+ power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
+ final double screenFullPower =
+ mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
+ for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+ double screenBinPower = screenFullPower * (i + 0.5f)
+ / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
+ long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType)
+ / 1000;
+ double p = screenBinPower*brightnessTime;
+ if (DEBUG && p != 0) {
+ Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
+ + " power=" + makemAh(p / (60 * 60 * 1000)));
+ }
+ power += p;
+ }
+ power /= (60*60*1000); // To hours
+ if (power != 0) {
+ addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
+ }
+ }
+
+ private void addRadioUsage() {
+ double power = 0;
+ final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+ long signalTimeMs = 0;
+ long noCoverageTimeMs = 0;
+ for (int i = 0; i < BINS; i++) {
+ long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, mRawRealtime, mStatsType)
+ / 1000;
+ double p = (strengthTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i))
+ / (60*60*1000);
+ if (DEBUG && p != 0) {
+ Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
+ + makemAh(p));
+ }
+ power += p;
+ signalTimeMs += strengthTimeMs;
+ if (i == 0) {
+ noCoverageTimeMs = strengthTimeMs;
+ }
+ }
+ long scanningTimeMs = mStats.getPhoneSignalScanningTime(mRawRealtime, mStatsType)
+ / 1000;
+ double p = (scanningTimeMs * mPowerProfile.getAveragePower(
+ PowerProfile.POWER_RADIO_SCANNING))
+ / (60*60*1000);
+ if (DEBUG && p != 0) {
+ Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + makemAh(p));
+ }
+ power += p;
+ long radioActiveTimeUs = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType);
+ long remainingActiveTime = (radioActiveTimeUs - mAppMobileActive) / 1000;
+ if (remainingActiveTime > 0) {
+ power += getMobilePowerPerMs() * remainingActiveTime;
+ }
+ if (power != 0) {
+ BatterySipper bs =
+ addEntry(BatterySipper.DrainType.CELL, signalTimeMs, power);
+ if (signalTimeMs != 0) {
+ bs.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
+ }
+ bs.mobileActive = remainingActiveTime;
+ bs.mobileActiveCount = mStats.getMobileRadioActiveUnknownCount(mStatsType);
+ }
+ }
+
+ private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
+ for (int i=0; i<from.size(); i++) {
+ BatterySipper wbs = from.get(i);
+ if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime);
+ bs.cpuTime += wbs.cpuTime;
+ bs.gpsTime += wbs.gpsTime;
+ bs.wifiRunningTime += wbs.wifiRunningTime;
+ bs.cpuFgTime += wbs.cpuFgTime;
+ bs.wakeLockTime += wbs.wakeLockTime;
+ bs.mobileRxPackets += wbs.mobileRxPackets;
+ bs.mobileTxPackets += wbs.mobileTxPackets;
+ bs.mobileActive += wbs.mobileActive;
+ bs.mobileActiveCount += wbs.mobileActiveCount;
+ bs.wifiRxPackets += wbs.wifiRxPackets;
+ bs.wifiTxPackets += wbs.wifiTxPackets;
+ bs.mobileRxBytes += wbs.mobileRxBytes;
+ bs.mobileTxBytes += wbs.mobileTxBytes;
+ bs.wifiRxBytes += wbs.wifiRxBytes;
+ bs.wifiTxBytes += wbs.wifiTxBytes;
+ }
+ bs.computeMobilemspp();
+ }
+
+ private void addWiFiUsage() {
+ long onTimeMs = mStats.getWifiOnTime(mRawRealtime, mStatsType) / 1000;
+ long runningTimeMs = mStats.getGlobalWifiRunningTime(mRawRealtime, mStatsType) / 1000;
+ if (DEBUG) Log.d(TAG, "WIFI runningTime=" + runningTimeMs
+ + " app runningTime=" + mAppWifiRunning);
+ runningTimeMs -= mAppWifiRunning;
+ if (runningTimeMs < 0) runningTimeMs = 0;
+ double wifiPower = (onTimeMs * 0 /* TODO */
+ * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)
+ + runningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON))
+ / (60*60*1000);
+ if (DEBUG && wifiPower != 0) {
+ Log.d(TAG, "Wifi: time=" + runningTimeMs + " power=" + makemAh(wifiPower));
+ }
+ if ((wifiPower+mWifiPower) != 0) {
+ BatterySipper bs = addEntry(BatterySipper.DrainType.WIFI, runningTimeMs,
+ wifiPower + mWifiPower);
+ aggregateSippers(bs, mWifiSippers, "WIFI");
+ }
+ }
+
+ private void addIdleUsage() {
+ long idleTimeMs = (mTypeBatteryRealtime
+ - mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000;
+ double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE))
+ / (60*60*1000);
+ if (DEBUG && idlePower != 0) {
+ Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower));
+ }
+ if (idlePower != 0) {
+ addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower);
+ }
+ }
+
+ private void addBluetoothUsage() {
+ long btOnTimeMs = mStats.getBluetoothOnTime(mRawRealtime, mStatsType) / 1000;
+ double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON)
+ / (60*60*1000);
+ if (DEBUG && btPower != 0) {
+ Log.d(TAG, "Bluetooth: time=" + btOnTimeMs + " power=" + makemAh(btPower));
+ }
+ int btPingCount = mStats.getBluetoothPingCount();
+ double pingPower = (btPingCount
+ * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD))
+ / (60*60*1000);
+ if (DEBUG && pingPower != 0) {
+ Log.d(TAG, "Bluetooth ping: count=" + btPingCount + " power=" + makemAh(pingPower));
+ }
+ btPower += pingPower;
+ if ((btPower+mBluetoothPower) != 0) {
+ BatterySipper bs = addEntry(BatterySipper.DrainType.BLUETOOTH, btOnTimeMs,
+ btPower + mBluetoothPower);
+ aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
+ }
+ }
+
+ private void addUserUsage() {
+ for (int i=0; i<mUserSippers.size(); i++) {
+ final int userId = mUserSippers.keyAt(i);
+ final List<BatterySipper> sippers = mUserSippers.valueAt(i);
+ Double userPower = mUserPower.get(userId);
+ double power = (userPower != null) ? userPower : 0.0;
+ BatterySipper bs = addEntry(BatterySipper.DrainType.USER, 0, power);
+ bs.userId = userId;
+ aggregateSippers(bs, sippers, "User");
+ }
+ }
+
+ /**
+ * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
+ */
+ private double getMobilePowerPerPacket() {
+ final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
+ final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
+ / 3600;
+
+ final long mobileRx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
+ final long mobileTx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
+ final long mobileData = mobileRx + mobileTx;
+
+ final long radioDataUptimeMs
+ = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType) / 1000;
+ final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
+ ? (mobileData / (double)radioDataUptimeMs)
+ : (((double)MOBILE_BPS) / 8 / 2048);
+
+ return (MOBILE_POWER / mobilePps) / (60*60);
+ }
+
+ /**
+ * Return estimated power (in mAs) of keeping the radio up
+ */
+ private double getMobilePowerPerMs() {
+ return mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) / (60*60*1000);
+ }
+
+ /**
+ * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
+ */
+ private double getWifiPowerPerPacket() {
+ final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
+ final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
+ / 3600;
+ return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
+ }
+
+ private void processMiscUsage() {
+ addUserUsage();
+ addPhoneUsage();
+ addScreenUsage();
+ addWiFiUsage();
+ addBluetoothUsage();
+ addIdleUsage(); // Not including cellular idle power
+ // Don't compute radio usage if it's a wifi-only device
+ ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
+ addRadioUsage();
+ }
+ }
+
+ private BatterySipper addEntry(DrainType drainType, long time, double power) {
+ mComputedPower += power;
+ return addEntryNoTotal(drainType, time, power);
+ }
+
+ private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) {
+ if (power > mMaxPower) mMaxPower = power;
+ BatterySipper bs = new BatterySipper(drainType, null, new double[] {power});
+ bs.usageTime = time;
+ mUsageList.add(bs);
+ return bs;
+ }
+
+ public List<BatterySipper> getUsageList() {
+ return mUsageList;
+ }
+
+ public List<BatterySipper> getMobilemsppList() {
+ return mMobilemsppList;
+ }
+
+ public long getStatsPeriod() { return mStatsPeriod; }
+
+ public int getStatsType() { return mStatsType; };
+
+ public double getMaxPower() { return mMaxPower; }
+
+ public double getTotalPower() { return mTotalPower; }
+
+ public double getComputedPower() { return mComputedPower; }
+
+ public double getMinDrainedPower() {
+ return mMinDrainedPower;
+ }
+
+ public double getMaxDrainedPower() {
+ return mMaxDrainedPower;
+ }
+
+ public long getBatteryTimeRemaining() { return mBatteryTimeRemaining; }
+
+ public long getChargeTimeRemaining() { return mChargeTimeRemaining; }
+
+ private void load() {
+ if (mBatteryInfo == null) {
+ return;
+ }
+ try {
+ byte[] data = mBatteryInfo.getStatistics();
+ Parcel parcel = Parcel.obtain();
+ parcel.unmarshall(data, 0, data.length);
+ parcel.setDataPosition(0);
+ BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
+ .createFromParcel(parcel);
+ stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED);
+ mStats = stats;
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException:", e);
+ }
+ if (mCollectBatteryBroadcast) {
+ mBatteryBroadcast = mContext.registerReceiver(null,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9c82fac..ed9f9bc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,12 +16,15 @@
package com.android.internal.os;
+import static android.net.NetworkStats.UID_ALL;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
+import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkStats;
+import android.os.BadParcelableException;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.FileUtils;
@@ -35,6 +38,7 @@ import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.WorkSource;
+import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
@@ -44,25 +48,23 @@ import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.JournaledFile;
-import com.google.android.collect.Sets;
-import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -86,7 +88,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 68 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 106 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -142,6 +144,11 @@ public final class BatteryStatsImpl extends BatteryStats {
private BatteryCallback mCallback;
/**
+ * Mapping isolated uids to the actual owning app uid.
+ */
+ final SparseIntArray mIsolatedUids = new SparseIntArray();
+
+ /**
* The statistics we have collected organized by uids.
*/
final SparseArray<BatteryStatsImpl.Uid> mUidStats =
@@ -168,13 +175,23 @@ public final class BatteryStatsImpl extends BatteryStats {
// These are the objects that will want to do something when the device
// is unplugged from power.
- final ArrayList<Unpluggable> mUnpluggables = new ArrayList<Unpluggable>();
+ final TimeBase mOnBatteryTimeBase = new TimeBase();
+
+ // These are the objects that will want to do something when the device
+ // is unplugged from power *and* the screen is off.
+ final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
+
+ // Set to true when we want to distribute CPU across wakelocks for the next
+ // CPU update, even if we aren't currently running wake locks.
+ boolean mDistributeWakelockCpu;
boolean mShuttingDown;
+ final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
+
long mHistoryBaseTime;
boolean mHaveBatteryLevel = false;
- boolean mRecordingHistory = true;
+ boolean mRecordingHistory = false;
int mNumHistoryItems;
static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB
@@ -183,9 +200,18 @@ public final class BatteryStatsImpl extends BatteryStats {
final HistoryItem mHistoryLastWritten = new HistoryItem();
final HistoryItem mHistoryLastLastWritten = new HistoryItem();
final HistoryItem mHistoryReadTmp = new HistoryItem();
+ final HistoryItem mHistoryAddTmp = new HistoryItem();
+ final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<HistoryTag, Integer>();
+ String[] mReadHistoryStrings;
+ int[] mReadHistoryUids;
+ int mReadHistoryChars;
+ int mNextHistoryTagIdx = 0;
+ int mNumHistoryTagChars = 0;
int mHistoryBufferLastPos = -1;
boolean mHistoryOverflow = false;
- long mLastHistoryTime = 0;
+ long mLastHistoryElapsedRealtime = 0;
+ long mTrackRunningHistoryElapsedRealtime = 0;
+ long mTrackRunningHistoryUptime = 0;
final HistoryItem mHistoryCur = new HistoryItem();
@@ -200,17 +226,17 @@ public final class BatteryStatsImpl extends BatteryStats {
int mStartCount;
- long mBatteryUptime;
- long mBatteryLastUptime;
- long mBatteryRealtime;
- long mBatteryLastRealtime;
+ long mStartClockTime;
long mUptime;
long mUptimeStart;
- long mLastUptime;
long mRealtime;
long mRealtimeStart;
- long mLastRealtime;
+
+ int mWakeLockNesting;
+ boolean mWakeLockImportant;
+ boolean mRecordAllWakeLocks;
+ boolean mNoAutoReset;
int mScreenState = Display.STATE_UNKNOWN;
StopwatchTimer mScreenOnTimer;
@@ -221,6 +247,9 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean mInteractive;
StopwatchTimer mInteractiveTimer;
+ boolean mLowPowerModeEnabled;
+ StopwatchTimer mLowPowerModeEnabledTimer;
+
boolean mPhoneOn;
StopwatchTimer mPhoneOnTimer;
@@ -241,19 +270,33 @@ public final class BatteryStatsImpl extends BatteryStats {
final StopwatchTimer[] mPhoneDataConnectionsTimer =
new StopwatchTimer[NUM_DATA_CONNECTION_TYPES];
- final LongSamplingCounter[] mNetworkActivityCounters =
+ final LongSamplingCounter[] mNetworkByteActivityCounters =
+ new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+ final LongSamplingCounter[] mNetworkPacketActivityCounters =
new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
boolean mWifiOn;
StopwatchTimer mWifiOnTimer;
- int mWifiOnUid = -1;
boolean mGlobalWifiRunning;
StopwatchTimer mGlobalWifiRunningTimer;
+ int mWifiState = -1;
+ final StopwatchTimer[] mWifiStateTimer = new StopwatchTimer[NUM_WIFI_STATES];
+
boolean mBluetoothOn;
StopwatchTimer mBluetoothOnTimer;
+ int mBluetoothState = -1;
+ final StopwatchTimer[] mBluetoothStateTimer = new StopwatchTimer[NUM_BLUETOOTH_STATES];
+
+ int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ StopwatchTimer mMobileRadioActiveTimer;
+ StopwatchTimer mMobileRadioActivePerAppTimer;
+ LongSamplingCounter mMobileRadioActiveAdjustedTime;
+ LongSamplingCounter mMobileRadioActiveUnknownTime;
+ LongSamplingCounter mMobileRadioActiveUnknownCount;
+
/** Bluetooth headset object */
BluetoothHeadset mBtHeadset;
@@ -263,20 +306,15 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
boolean mOnBattery;
boolean mOnBatteryInternal;
- long mTrackBatteryPastUptime;
- long mTrackBatteryUptimeStart;
- long mTrackBatteryPastRealtime;
- long mTrackBatteryRealtimeStart;
-
- long mUnpluggedBatteryUptime;
- long mUnpluggedBatteryRealtime;
/*
* These keep track of battery levels (1-100) at the last plug event and the last unplug event.
*/
int mDischargeStartLevel;
int mDischargeUnplugLevel;
+ int mDischargePlugLevel;
int mDischargeCurrentLevel;
+ int mCurrentBatteryLevel;
int mLowDischargeAmountSinceCharge;
int mHighDischargeAmountSinceCharge;
int mDischargeScreenOnUnplugLevel;
@@ -286,10 +324,21 @@ public final class BatteryStatsImpl extends BatteryStats {
int mDischargeAmountScreenOff;
int mDischargeAmountScreenOffSinceCharge;
- long mLastWriteTime = 0; // Milliseconds
+ static final int MAX_LEVEL_STEPS = 100;
+
+ int mLastDischargeStepLevel;
+ long mLastDischargeStepTime;
+ int mMinDischargeStepLevel;
+ int mNumDischargeStepDurations;
+ final long[] mDischargeStepDurations = new long[MAX_LEVEL_STEPS];
- private long mRadioDataUptime;
- private long mRadioDataStart;
+ int mLastChargeStepLevel;
+ long mLastChargeStepTime;
+ int mMaxChargeStepLevel;
+ int mNumChargeStepDurations;
+ final long[] mChargeStepDurations = new long[MAX_LEVEL_STEPS];
+
+ long mLastWriteTime = 0; // Milliseconds
private int mBluetoothPingCount;
private int mBluetoothPingStart = -1;
@@ -304,12 +353,21 @@ public final class BatteryStatsImpl extends BatteryStats {
private final HashMap<String, SamplingTimer> mKernelWakelockStats =
new HashMap<String, SamplingTimer>();
- public Map<String, ? extends SamplingTimer> getKernelWakelockStats() {
+ public Map<String, ? extends Timer> getKernelWakelockStats() {
return mKernelWakelockStats;
}
private static int sKernelWakelockUpdateVersion = 0;
+ String mLastWakeupReason = null;
+ long mLastWakeupUptimeMs = 0;
+ private final HashMap<String, LongSamplingCounter> mWakeupReasonStats =
+ new HashMap<String, LongSamplingCounter>();
+
+ public Map<String, ? extends LongCounter> getWakeupReasonStats() {
+ return mWakeupReasonStats;
+ }
+
private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name
Process.PROC_QUOTES,
@@ -342,15 +400,18 @@ public final class BatteryStatsImpl extends BatteryStats {
private final Map<String, KernelWakelockStats> mProcWakelockFileStats =
new HashMap<String, KernelWakelockStats>();
- private HashMap<String, Integer> mUidCache = new HashMap<String, Integer>();
-
private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
- private NetworkStats mLastSnapshot;
+ private NetworkStats mCurMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
+ private NetworkStats mLastMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
+ private NetworkStats mCurWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
+ private NetworkStats mLastWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
+ private NetworkStats mTmpNetworkStats;
+ private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
@GuardedBy("this")
- private HashSet<String> mMobileIfaces = Sets.newHashSet();
+ private String[] mMobileIfaces = new String[0];
@GuardedBy("this")
- private HashSet<String> mWifiIfaces = Sets.newHashSet();
+ private String[] mWifiIfaces = new String[0];
// For debugging
public BatteryStatsImpl() {
@@ -358,35 +419,228 @@ public final class BatteryStatsImpl extends BatteryStats {
mHandler = null;
}
- public static interface Unpluggable {
- void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime);
- void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime);
+ public static interface TimeBaseObs {
+ void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime);
+ void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime);
+ }
+
+ static class TimeBase {
+ private final ArrayList<TimeBaseObs> mObservers = new ArrayList<TimeBaseObs>();
+
+ private long mUptime;
+ private long mRealtime;
+
+ private boolean mRunning;
+
+ private long mPastUptime;
+ private long mUptimeStart;
+ private long mPastRealtime;
+ private long mRealtimeStart;
+ private long mUnpluggedUptime;
+ private long mUnpluggedRealtime;
+
+ public void dump(PrintWriter pw, String prefix) {
+ StringBuilder sb = new StringBuilder(128);
+ pw.print(prefix); pw.print("mRunning="); pw.println(mRunning);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append("mUptime=");
+ formatTimeMs(sb, mUptime / 1000);
+ pw.println(sb.toString());
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append("mRealtime=");
+ formatTimeMs(sb, mRealtime / 1000);
+ pw.println(sb.toString());
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append("mPastUptime=");
+ formatTimeMs(sb, mPastUptime / 1000); sb.append("mUptimeStart=");
+ formatTimeMs(sb, mUptimeStart / 1000);
+ sb.append("mUnpluggedUptime="); formatTimeMs(sb, mUnpluggedUptime / 1000);
+ pw.println(sb.toString());
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append("mPastRealtime=");
+ formatTimeMs(sb, mPastRealtime / 1000); sb.append("mRealtimeStart=");
+ formatTimeMs(sb, mRealtimeStart / 1000);
+ sb.append("mUnpluggedRealtime="); formatTimeMs(sb, mUnpluggedRealtime / 1000);
+ pw.println(sb.toString());
+ }
+
+ public void add(TimeBaseObs observer) {
+ mObservers.add(observer);
+ }
+
+ public void remove(TimeBaseObs observer) {
+ if (!mObservers.remove(observer)) {
+ Slog.wtf(TAG, "Removed unknown observer: " + observer);
+ }
+ }
+
+ public void init(long uptime, long realtime) {
+ mRealtime = 0;
+ mUptime = 0;
+ mPastUptime = 0;
+ mPastRealtime = 0;
+ mUptimeStart = uptime;
+ mRealtimeStart = realtime;
+ mUnpluggedUptime = getUptime(mUptimeStart);
+ mUnpluggedRealtime = getRealtime(mRealtimeStart);
+ }
+
+ public void reset(long uptime, long realtime) {
+ if (!mRunning) {
+ mPastUptime = 0;
+ mPastRealtime = 0;
+ } else {
+ mUptimeStart = uptime;
+ mRealtimeStart = realtime;
+ mUnpluggedUptime = getUptime(uptime);
+ mUnpluggedRealtime = getRealtime(realtime);
+ }
+ }
+
+ public long computeUptime(long curTime, int which) {
+ switch (which) {
+ case STATS_SINCE_CHARGED:
+ return mUptime + getUptime(curTime);
+ case STATS_CURRENT:
+ return getUptime(curTime);
+ case STATS_SINCE_UNPLUGGED:
+ return getUptime(curTime) - mUnpluggedUptime;
+ }
+ return 0;
+ }
+
+ public long computeRealtime(long curTime, int which) {
+ switch (which) {
+ case STATS_SINCE_CHARGED:
+ return mRealtime + getRealtime(curTime);
+ case STATS_CURRENT:
+ return getRealtime(curTime);
+ case STATS_SINCE_UNPLUGGED:
+ return getRealtime(curTime) - mUnpluggedRealtime;
+ }
+ return 0;
+ }
+
+ public long getUptime(long curTime) {
+ long time = mPastUptime;
+ if (mRunning) {
+ time += curTime - mUptimeStart;
+ }
+ return time;
+ }
+
+ public long getRealtime(long curTime) {
+ long time = mPastRealtime;
+ if (mRunning) {
+ time += curTime - mRealtimeStart;
+ }
+ return time;
+ }
+
+ public long getUptimeStart() {
+ return mUptimeStart;
+ }
+
+ public long getRealtimeStart() {
+ return mRealtimeStart;
+ }
+
+ public boolean isRunning() {
+ return mRunning;
+ }
+
+ public boolean setRunning(boolean running, long uptime, long realtime) {
+ if (mRunning != running) {
+ mRunning = running;
+ if (running) {
+ mUptimeStart = uptime;
+ mRealtimeStart = realtime;
+ long batteryUptime = mUnpluggedUptime = getUptime(uptime);
+ long batteryRealtime = mUnpluggedRealtime = getRealtime(realtime);
+
+ for (int i = mObservers.size() - 1; i >= 0; i--) {
+ mObservers.get(i).onTimeStarted(realtime, batteryUptime, batteryRealtime);
+ }
+ } else {
+ mPastUptime += uptime - mUptimeStart;
+ mPastRealtime += realtime - mRealtimeStart;
+
+ long batteryUptime = getUptime(uptime);
+ long batteryRealtime = getRealtime(realtime);
+
+ for (int i = mObservers.size() - 1; i >= 0; i--) {
+ mObservers.get(i).onTimeStopped(realtime, batteryUptime, batteryRealtime);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public void readSummaryFromParcel(Parcel in) {
+ mUptime = in.readLong();
+ mRealtime = in.readLong();
+ }
+
+ public void writeSummaryToParcel(Parcel out, long uptime, long realtime) {
+ out.writeLong(computeUptime(uptime, STATS_SINCE_CHARGED));
+ out.writeLong(computeRealtime(realtime, STATS_SINCE_CHARGED));
+ }
+
+ public void readFromParcel(Parcel in) {
+ mRunning = false;
+ mUptime = in.readLong();
+ mPastUptime = in.readLong();
+ mUptimeStart = in.readLong();
+ mRealtime = in.readLong();
+ mPastRealtime = in.readLong();
+ mRealtimeStart = in.readLong();
+ mUnpluggedUptime = in.readLong();
+ mUnpluggedRealtime = in.readLong();
+ }
+
+ public void writeToParcel(Parcel out, long uptime, long realtime) {
+ final long runningUptime = getUptime(uptime);
+ final long runningRealtime = getRealtime(realtime);
+ out.writeLong(mUptime);
+ out.writeLong(runningUptime);
+ out.writeLong(mUptimeStart);
+ out.writeLong(mRealtime);
+ out.writeLong(runningRealtime);
+ out.writeLong(mRealtimeStart);
+ out.writeLong(mUnpluggedUptime);
+ out.writeLong(mUnpluggedRealtime);
+ }
}
/**
* State for keeping track of counting information.
*/
- public static class Counter extends BatteryStats.Counter implements Unpluggable {
+ public static class Counter extends BatteryStats.Counter implements TimeBaseObs {
final AtomicInteger mCount = new AtomicInteger();
- final ArrayList<Unpluggable> mUnpluggables;
+ final TimeBase mTimeBase;
int mLoadedCount;
int mLastCount;
int mUnpluggedCount;
int mPluggedCount;
- Counter(ArrayList<Unpluggable> unpluggables, Parcel in) {
- mUnpluggables = unpluggables;
+ Counter(TimeBase timeBase, Parcel in) {
+ mTimeBase = timeBase;
mPluggedCount = in.readInt();
mCount.set(mPluggedCount);
mLoadedCount = in.readInt();
mLastCount = 0;
mUnpluggedCount = in.readInt();
- unpluggables.add(this);
+ timeBase.add(this);
}
- Counter(ArrayList<Unpluggable> unpluggables) {
- mUnpluggables = unpluggables;
- unpluggables.add(this);
+ Counter(TimeBase timeBase) {
+ mTimeBase = timeBase;
+ timeBase.add(this);
}
public void writeToParcel(Parcel out) {
@@ -395,12 +649,12 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(mUnpluggedCount);
}
- public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
mUnpluggedCount = mPluggedCount;
mCount.set(mPluggedCount);
}
- public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
mPluggedCount = mCount.get();
}
@@ -422,16 +676,11 @@ public final class BatteryStatsImpl extends BatteryStats {
@Override
public int getCountLocked(int which) {
- int val;
- if (which == STATS_LAST) {
- val = mLastCount;
- } else {
- val = mCount.get();
- if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedCount;
- } else if (which != STATS_SINCE_CHARGED) {
- val -= mLoadedCount;
- }
+ int val = mCount.get();
+ if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedCount;
+ } else if (which != STATS_SINCE_CHARGED) {
+ val -= mLoadedCount;
}
return val;
@@ -460,7 +709,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
void detach() {
- mUnpluggables.remove(this);
+ mTimeBase.remove(this);
}
void writeSummaryFromParcelLocked(Parcel out) {
@@ -477,12 +726,12 @@ public final class BatteryStatsImpl extends BatteryStats {
}
public static class SamplingCounter extends Counter {
- SamplingCounter(ArrayList<Unpluggable> unpluggables, Parcel in) {
- super(unpluggables, in);
+ SamplingCounter(TimeBase timeBase, Parcel in) {
+ super(timeBase, in);
}
- SamplingCounter(ArrayList<Unpluggable> unpluggables) {
- super(unpluggables);
+ SamplingCounter(TimeBase timeBase) {
+ super(timeBase);
}
public void addCountAtomic(long count) {
@@ -490,27 +739,27 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- public static class LongSamplingCounter implements Unpluggable {
- final ArrayList<Unpluggable> mUnpluggables;
+ public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
+ final TimeBase mTimeBase;
long mCount;
long mLoadedCount;
long mLastCount;
long mUnpluggedCount;
long mPluggedCount;
- LongSamplingCounter(ArrayList<Unpluggable> unpluggables, Parcel in) {
- mUnpluggables = unpluggables;
+ LongSamplingCounter(TimeBase timeBase, Parcel in) {
+ mTimeBase = timeBase;
mPluggedCount = in.readLong();
mCount = mPluggedCount;
mLoadedCount = in.readLong();
mLastCount = 0;
mUnpluggedCount = in.readLong();
- unpluggables.add(this);
+ timeBase.add(this);
}
- LongSamplingCounter(ArrayList<Unpluggable> unpluggables) {
- mUnpluggables = unpluggables;
- unpluggables.add(this);
+ LongSamplingCounter(TimeBase timeBase) {
+ mTimeBase = timeBase;
+ timeBase.add(this);
}
public void writeToParcel(Parcel out) {
@@ -520,32 +769,35 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
- public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
mUnpluggedCount = mPluggedCount;
mCount = mPluggedCount;
}
@Override
- public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
mPluggedCount = mCount;
}
public long getCountLocked(int which) {
- long val;
- if (which == STATS_LAST) {
- val = mLastCount;
- } else {
- val = mCount;
- if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedCount;
- } else if (which != STATS_SINCE_CHARGED) {
- val -= mLoadedCount;
- }
+ long val = mCount;
+ if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedCount;
+ } else if (which != STATS_SINCE_CHARGED) {
+ val -= mLoadedCount;
}
return val;
}
+ @Override
+ public void logState(Printer pw, String prefix) {
+ pw.println(prefix + "mCount=" + mCount
+ + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
+ + " mUnpluggedCount=" + mUnpluggedCount
+ + " mPluggedCount=" + mPluggedCount);
+ }
+
void addCountLocked(long count) {
mCount += count;
}
@@ -562,7 +814,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
void detach() {
- mUnpluggables.remove(this);
+ mTimeBase.remove(this);
}
void writeSummaryFromParcelLocked(Parcel out) {
@@ -580,9 +832,9 @@ public final class BatteryStatsImpl extends BatteryStats {
/**
* State for keeping track of timing information.
*/
- public static abstract class Timer extends BatteryStats.Timer implements Unpluggable {
+ public static abstract class Timer extends BatteryStats.Timer implements TimeBaseObs {
final int mType;
- final ArrayList<Unpluggable> mUnpluggables;
+ final TimeBase mTimeBase;
int mCount;
int mLoadedCount;
@@ -621,12 +873,12 @@ public final class BatteryStatsImpl extends BatteryStats {
/**
* Constructs from a parcel.
* @param type
- * @param unpluggables
+ * @param timeBase
* @param in
*/
- Timer(int type, ArrayList<Unpluggable> unpluggables, Parcel in) {
+ Timer(int type, TimeBase timeBase, Parcel in) {
mType = type;
- mUnpluggables = unpluggables;
+ mTimeBase = timeBase;
mCount = in.readInt();
mLoadedCount = in.readInt();
@@ -636,13 +888,14 @@ public final class BatteryStatsImpl extends BatteryStats {
mLoadedTime = in.readLong();
mLastTime = 0;
mUnpluggedTime = in.readLong();
- unpluggables.add(this);
+ timeBase.add(this);
+ if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTime);
}
- Timer(int type, ArrayList<Unpluggable> unpluggables) {
+ Timer(int type, TimeBase timeBase) {
mType = type;
- mUnpluggables = unpluggables;
- unpluggables.add(this);
+ mTimeBase = timeBase;
+ timeBase.add(this);
}
protected abstract long computeRunTimeLocked(long curBatteryRealtime);
@@ -653,7 +906,7 @@ public final class BatteryStatsImpl extends BatteryStats {
* Clear state of this timer. Returns true if the timer is inactive
* so can be completely dropped.
*/
- boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
+ boolean reset(boolean detachIfReset) {
mTotalTime = mLoadedTime = mLastTime = 0;
mCount = mLoadedCount = mLastCount = 0;
if (detachIfReset) {
@@ -663,25 +916,27 @@ public final class BatteryStatsImpl extends BatteryStats {
}
void detach() {
- mUnpluggables.remove(this);
+ mTimeBase.remove(this);
}
- public void writeToParcel(Parcel out, long batteryRealtime) {
+ public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
+ if (DEBUG) Log.i(TAG, "**** WRITING TIMER #" + mType + ": mTotalTime="
+ + computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs)));
out.writeInt(mCount);
out.writeInt(mLoadedCount);
out.writeInt(mUnpluggedCount);
- out.writeLong(computeRunTimeLocked(batteryRealtime));
+ out.writeLong(computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs)));
out.writeLong(mLoadedTime);
out.writeLong(mUnpluggedTime);
}
- public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStarted(long elapsedRealtime, long timeBaseUptime, long baseRealtime) {
if (DEBUG && mType < 0) {
- Log.v(TAG, "unplug #" + mType + ": realtime=" + batteryRealtime
+ Log.v(TAG, "unplug #" + mType + ": realtime=" + baseRealtime
+ " old mUnpluggedTime=" + mUnpluggedTime
+ " old mUnpluggedCount=" + mUnpluggedCount);
}
- mUnpluggedTime = computeRunTimeLocked(batteryRealtime);
+ mUnpluggedTime = computeRunTimeLocked(baseRealtime);
mUnpluggedCount = mCount;
if (DEBUG && mType < 0) {
Log.v(TAG, "unplug #" + mType
@@ -690,12 +945,12 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
if (DEBUG && mType < 0) {
- Log.v(TAG, "plug #" + mType + ": realtime=" + batteryRealtime
+ Log.v(TAG, "plug #" + mType + ": realtime=" + baseRealtime
+ " old mTotalTime=" + mTotalTime);
}
- mTotalTime = computeRunTimeLocked(batteryRealtime);
+ mTotalTime = computeRunTimeLocked(baseRealtime);
mCount = computeCurrentCountLocked();
if (DEBUG && mType < 0) {
Log.v(TAG, "plug #" + mType
@@ -709,29 +964,23 @@ public final class BatteryStatsImpl extends BatteryStats {
* @param out the Parcel to be written to.
* @param timer a Timer, or null.
*/
- public static void writeTimerToParcel(Parcel out, Timer timer,
- long batteryRealtime) {
+ public static void writeTimerToParcel(Parcel out, Timer timer, long elapsedRealtimeUs) {
if (timer == null) {
out.writeInt(0); // indicates null
return;
}
out.writeInt(1); // indicates non-null
- timer.writeToParcel(out, batteryRealtime);
+ timer.writeToParcel(out, elapsedRealtimeUs);
}
@Override
- public long getTotalTimeLocked(long batteryRealtime, int which) {
- long val;
- if (which == STATS_LAST) {
- val = mLastTime;
- } else {
- val = computeRunTimeLocked(batteryRealtime);
- if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedTime;
- } else if (which != STATS_SINCE_CHARGED) {
- val -= mLoadedTime;
- }
+ public long getTotalTimeLocked(long elapsedRealtimeUs, int which) {
+ long val = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs));
+ if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedTime;
+ } else if (which != STATS_SINCE_CHARGED) {
+ val -= mLoadedTime;
}
return val;
@@ -739,16 +988,11 @@ public final class BatteryStatsImpl extends BatteryStats {
@Override
public int getCountLocked(int which) {
- int val;
- if (which == STATS_LAST) {
- val = mLastCount;
- } else {
- val = computeCurrentCountLocked();
- if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedCount;
- } else if (which != STATS_SINCE_CHARGED) {
- val -= mLoadedCount;
- }
+ int val = computeCurrentCountLocked();
+ if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedCount;
+ } else if (which != STATS_SINCE_CHARGED) {
+ val -= mLoadedCount;
}
return val;
@@ -765,16 +1009,15 @@ public final class BatteryStatsImpl extends BatteryStats {
}
- void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) {
- long runTime = computeRunTimeLocked(batteryRealtime);
- // Divide by 1000 for backwards compatibility
- out.writeLong((runTime + 500) / 1000);
+ void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) {
+ long runTime = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs));
+ out.writeLong(runTime);
out.writeInt(mCount);
}
void readSummaryFromParcelLocked(Parcel in) {
// Multiply by 1000 for backwards compatibility
- mTotalTime = mLoadedTime = in.readLong() * 1000;
+ mTotalTime = mLoadedTime = in.readLong();
mLastTime = 0;
mUnpluggedTime = mTotalTime;
mCount = mLoadedCount = in.readInt();
@@ -811,7 +1054,7 @@ public final class BatteryStatsImpl extends BatteryStats {
/**
* Whether we are currently in a discharge cycle.
*/
- boolean mInDischarge;
+ boolean mTimeBaseRunning;
/**
* Whether we are currently recording reported values.
@@ -823,21 +1066,20 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
int mUpdateVersion;
- SamplingTimer(ArrayList<Unpluggable> unpluggables, boolean inDischarge, Parcel in) {
- super(0, unpluggables, in);
+ SamplingTimer(TimeBase timeBase, Parcel in) {
+ super(0, timeBase, in);
mCurrentReportedCount = in.readInt();
mUnpluggedReportedCount = in.readInt();
mCurrentReportedTotalTime = in.readLong();
mUnpluggedReportedTotalTime = in.readLong();
mTrackingReportedValues = in.readInt() == 1;
- mInDischarge = inDischarge;
+ mTimeBaseRunning = timeBase.isRunning();
}
- SamplingTimer(ArrayList<Unpluggable> unpluggables, boolean inDischarge,
- boolean trackReportedValues) {
- super(0, unpluggables);
+ SamplingTimer(TimeBase timeBase, boolean trackReportedValues) {
+ super(0, timeBase);
mTrackingReportedValues = trackReportedValues;
- mInDischarge = inDischarge;
+ mTimeBaseRunning = timeBase.isRunning();
}
public void setStale() {
@@ -855,7 +1097,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
public void updateCurrentReportedCount(int count) {
- if (mInDischarge && mUnpluggedReportedCount == 0) {
+ if (mTimeBaseRunning && mUnpluggedReportedCount == 0) {
// Updating the reported value for the first time.
mUnpluggedReportedCount = count;
// If we are receiving an update update mTrackingReportedValues;
@@ -865,7 +1107,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
public void updateCurrentReportedTotalTime(long totalTime) {
- if (mInDischarge && mUnpluggedReportedTotalTime == 0) {
+ if (mTimeBaseRunning && mUnpluggedReportedTotalTime == 0) {
// Updating the reported value for the first time.
mUnpluggedReportedTotalTime = totalTime;
// If we are receiving an update update mTrackingReportedValues;
@@ -874,18 +1116,18 @@ public final class BatteryStatsImpl extends BatteryStats {
mCurrentReportedTotalTime = totalTime;
}
- public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
- super.unplug(elapsedRealtime, batteryUptime, batteryRealtime);
+ public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
+ super.onTimeStarted(elapsedRealtime, baseUptime, baseRealtime);
if (mTrackingReportedValues) {
mUnpluggedReportedTotalTime = mCurrentReportedTotalTime;
mUnpluggedReportedCount = mCurrentReportedCount;
}
- mInDischarge = true;
+ mTimeBaseRunning = true;
}
- public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
- super.plug(elapsedRealtime, batteryUptime, batteryRealtime);
- mInDischarge = false;
+ public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
+ super.onTimeStopped(elapsedRealtime, baseUptime, baseRealtime);
+ mTimeBaseRunning = false;
}
public void logState(Printer pw, String prefix) {
@@ -897,17 +1139,17 @@ public final class BatteryStatsImpl extends BatteryStats {
}
protected long computeRunTimeLocked(long curBatteryRealtime) {
- return mTotalTime + (mInDischarge && mTrackingReportedValues
+ return mTotalTime + (mTimeBaseRunning && mTrackingReportedValues
? mCurrentReportedTotalTime - mUnpluggedReportedTotalTime : 0);
}
protected int computeCurrentCountLocked() {
- return mCount + (mInDischarge && mTrackingReportedValues
+ return mCount + (mTimeBaseRunning && mTrackingReportedValues
? mCurrentReportedCount - mUnpluggedReportedCount : 0);
}
- public void writeToParcel(Parcel out, long batteryRealtime) {
- super.writeToParcel(out, batteryRealtime);
+ public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
+ super.writeToParcel(out, elapsedRealtimeUs);
out.writeInt(mCurrentReportedCount);
out.writeInt(mUnpluggedReportedCount);
out.writeLong(mCurrentReportedTotalTime);
@@ -915,8 +1157,8 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(mTrackingReportedValues ? 1 : 0);
}
- boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
- super.reset(stats, detachIfReset);
+ boolean reset(boolean detachIfReset) {
+ super.reset(detachIfReset);
setStale();
return true;
}
@@ -958,45 +1200,43 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
boolean mInDischarge;
- BatchTimer(Uid uid, int type, ArrayList<Unpluggable> unpluggables,
- boolean inDischarge, Parcel in) {
- super(type, unpluggables, in);
+ BatchTimer(Uid uid, int type, TimeBase timeBase, Parcel in) {
+ super(type, timeBase, in);
mUid = uid;
mLastAddedTime = in.readLong();
mLastAddedDuration = in.readLong();
- mInDischarge = inDischarge;
+ mInDischarge = timeBase.isRunning();
}
- BatchTimer(Uid uid, int type, ArrayList<Unpluggable> unpluggables,
- boolean inDischarge) {
- super(type, unpluggables);
+ BatchTimer(Uid uid, int type, TimeBase timeBase) {
+ super(type, timeBase);
mUid = uid;
- mInDischarge = inDischarge;
+ mInDischarge = timeBase.isRunning();
}
@Override
- public void writeToParcel(Parcel out, long batteryRealtime) {
- super.writeToParcel(out, batteryRealtime);
+ public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
+ super.writeToParcel(out, elapsedRealtimeUs);
out.writeLong(mLastAddedTime);
out.writeLong(mLastAddedDuration);
}
@Override
- public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
recomputeLastDuration(SystemClock.elapsedRealtime() * 1000, false);
mInDischarge = false;
- super.plug(elapsedRealtime, batteryUptime, batteryRealtime);
+ super.onTimeStopped(elapsedRealtime, baseUptime, baseRealtime);
}
@Override
- public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
recomputeLastDuration(elapsedRealtime, false);
mInDischarge = true;
// If we are still within the last added duration, then re-added whatever remains.
if (mLastAddedTime == elapsedRealtime) {
mTotalTime += mLastAddedDuration;
}
- super.unplug(elapsedRealtime, batteryUptime, batteryRealtime);
+ super.onTimeStarted(elapsedRealtime, baseUptime, baseRealtime);
}
@Override
@@ -1062,11 +1302,11 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
- boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
+ boolean reset(boolean detachIfReset) {
final long now = SystemClock.elapsedRealtime() * 1000;
recomputeLastDuration(now, true);
boolean stillActive = mLastAddedTime == now;
- super.reset(stats, !stillActive && detachIfReset);
+ super.reset(!stillActive && detachIfReset);
return !stillActive;
}
}
@@ -1102,16 +1342,16 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean mInList;
StopwatchTimer(Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
- ArrayList<Unpluggable> unpluggables, Parcel in) {
- super(type, unpluggables, in);
+ TimeBase timeBase, Parcel in) {
+ super(type, timeBase, in);
mUid = uid;
mTimerPool = timerPool;
mUpdateTime = in.readLong();
}
StopwatchTimer(Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
- ArrayList<Unpluggable> unpluggables) {
- super(type, unpluggables);
+ TimeBase timeBase) {
+ super(type, timeBase);
mUid = uid;
mTimerPool = timerPool;
}
@@ -1120,18 +1360,18 @@ public final class BatteryStatsImpl extends BatteryStats {
mTimeout = timeout;
}
- public void writeToParcel(Parcel out, long batteryRealtime) {
- super.writeToParcel(out, batteryRealtime);
+ public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
+ super.writeToParcel(out, elapsedRealtimeUs);
out.writeLong(mUpdateTime);
}
- public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
if (mNesting > 0) {
if (DEBUG && mType < 0) {
Log.v(TAG, "old mUpdateTime=" + mUpdateTime);
}
- super.plug(elapsedRealtime, batteryUptime, batteryRealtime);
- mUpdateTime = batteryRealtime;
+ super.onTimeStopped(elapsedRealtime, baseUptime, baseRealtime);
+ mUpdateTime = baseRealtime;
if (DEBUG && mType < 0) {
Log.v(TAG, "new mUpdateTime=" + mUpdateTime);
}
@@ -1144,14 +1384,14 @@ public final class BatteryStatsImpl extends BatteryStats {
+ " mAcquireTime=" + mAcquireTime);
}
- void startRunningLocked(BatteryStatsImpl stats) {
+ void startRunningLocked(long elapsedRealtimeMs) {
if (mNesting++ == 0) {
- mUpdateTime = stats.getBatteryRealtimeLocked(
- SystemClock.elapsedRealtime() * 1000);
+ final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
+ mUpdateTime = batteryRealtime;
if (mTimerPool != null) {
// Accumulate time to all currently active timers before adding
// this new one to the pool.
- refreshTimersLocked(stats, mTimerPool);
+ refreshTimersLocked(batteryRealtime, mTimerPool, null);
// Add this timer to the active pool
mTimerPool.add(this);
}
@@ -1170,21 +1410,39 @@ public final class BatteryStatsImpl extends BatteryStats {
return mNesting > 0;
}
- void stopRunningLocked(BatteryStatsImpl stats) {
+ long checkpointRunningLocked(long elapsedRealtimeMs) {
+ if (mNesting > 0) {
+ // We are running...
+ final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
+ if (mTimerPool != null) {
+ return refreshTimersLocked(batteryRealtime, mTimerPool, this);
+ }
+ final long heldTime = batteryRealtime - mUpdateTime;
+ mUpdateTime = batteryRealtime;
+ mTotalTime += heldTime;
+ return heldTime;
+ }
+ return 0;
+ }
+
+ long getLastUpdateTimeMs() {
+ return mUpdateTime;
+ }
+
+ void stopRunningLocked(long elapsedRealtimeMs) {
// Ignore attempt to stop a timer that isn't running
if (mNesting == 0) {
return;
}
if (--mNesting == 0) {
+ final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
if (mTimerPool != null) {
// Accumulate time to all active counters, scaled by the total
// active in the pool, before taking this one out of the pool.
- refreshTimersLocked(stats, mTimerPool);
+ refreshTimersLocked(batteryRealtime, mTimerPool, null);
// Remove this timer from the active pool
mTimerPool.remove(this);
} else {
- final long realtime = SystemClock.elapsedRealtime() * 1000;
- final long batteryRealtime = stats.getBatteryRealtimeLocked(realtime);
mNesting = 1;
mTotalTime = computeRunTimeLocked(batteryRealtime);
mNesting = 0;
@@ -1206,19 +1464,23 @@ public final class BatteryStatsImpl extends BatteryStats {
// Update the total time for all other running Timers with the same type as this Timer
// due to a change in timer count
- private static void refreshTimersLocked(final BatteryStatsImpl stats,
- final ArrayList<StopwatchTimer> pool) {
- final long realtime = SystemClock.elapsedRealtime() * 1000;
- final long batteryRealtime = stats.getBatteryRealtimeLocked(realtime);
+ private static long refreshTimersLocked(long batteryRealtime,
+ final ArrayList<StopwatchTimer> pool, StopwatchTimer self) {
+ long selfTime = 0;
final int N = pool.size();
for (int i=N-1; i>= 0; i--) {
final StopwatchTimer t = pool.get(i);
long heldTime = batteryRealtime - t.mUpdateTime;
if (heldTime > 0) {
- t.mTotalTime += heldTime / N;
+ final long myTime = heldTime / N;
+ if (t == self) {
+ selfTime = myTime;
+ }
+ t.mTotalTime += myTime;
}
t.mUpdateTime = batteryRealtime;
}
+ return selfTime;
}
@Override
@@ -1237,12 +1499,11 @@ public final class BatteryStatsImpl extends BatteryStats {
return mCount;
}
- boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
+ boolean reset(boolean detachIfReset) {
boolean canDetach = mNesting <= 0;
- super.reset(stats, canDetach && detachIfReset);
+ super.reset(canDetach && detachIfReset);
if (mNesting > 0) {
- mUpdateTime = stats.getBatteryRealtimeLocked(
- SystemClock.elapsedRealtime() * 1000);
+ mUpdateTime = mTimeBase.getRealtime(SystemClock.elapsedRealtime() * 1000);
}
mAcquireTime = mTotalTime;
return canDetach;
@@ -1261,6 +1522,19 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ /*
+ * Get the wakeup reason counter, and create a new one if one
+ * doesn't already exist.
+ */
+ public LongSamplingCounter getWakeupReasonCounterLocked(String name) {
+ LongSamplingCounter counter = mWakeupReasonStats.get(name);
+ if (counter == null) {
+ counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+ mWakeupReasonStats.put(name, counter);
+ }
+ return counter;
+ }
+
private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
FileInputStream is;
@@ -1405,51 +1679,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public SamplingTimer getKernelWakelockTimerLocked(String name) {
SamplingTimer kwlt = mKernelWakelockStats.get(name);
if (kwlt == null) {
- kwlt = new SamplingTimer(mUnpluggables, mOnBatteryInternal,
- true /* track reported values */);
+ kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase, true /* track reported values */);
mKernelWakelockStats.put(name, kwlt);
}
return kwlt;
}
- /**
- * Radio uptime in microseconds when transferring data. This value is very approximate.
- * @return
- */
- private long getCurrentRadioDataUptime() {
- try {
- File awakeTimeFile = new File("/sys/devices/virtual/net/rmnet0/awake_time_ms");
- if (!awakeTimeFile.exists()) return 0;
- BufferedReader br = new BufferedReader(new FileReader(awakeTimeFile));
- String line = br.readLine();
- br.close();
- return Long.parseLong(line) * 1000;
- } catch (NumberFormatException nfe) {
- // Nothing
- } catch (IOException ioe) {
- // Nothing
- }
- return 0;
- }
-
- /**
- * @deprecated use getRadioDataUptime
- */
- public long getRadioDataUptimeMs() {
- return getRadioDataUptime() / 1000;
- }
-
- /**
- * Returns the duration that the cell radio was up for data transfers.
- */
- public long getRadioDataUptime() {
- if (mRadioDataStart == -1) {
- return mRadioDataUptime;
- } else {
- return getCurrentRadioDataUptime() - mRadioDataStart;
- }
- }
-
private int getCurrentBluetoothPingCount() {
if (mBtHeadset != null) {
List<BluetoothDevice> deviceList = mBtHeadset.getConnectedDevices();
@@ -1476,83 +1711,434 @@ public final class BatteryStatsImpl extends BatteryStats {
mBtHeadset = headset;
}
- int mChangedBufferStates = 0;
+ private int writeHistoryTag(HistoryTag tag) {
+ Integer idxObj = mHistoryTagPool.get(tag);
+ int idx;
+ if (idxObj != null) {
+ idx = idxObj;
+ } else {
+ idx = mNextHistoryTagIdx;
+ HistoryTag key = new HistoryTag();
+ key.setTo(tag);
+ tag.poolIdx = idx;
+ mHistoryTagPool.put(key, idx);
+ mNextHistoryTagIdx++;
+ mNumHistoryTagChars += key.string.length() + 1;
+ }
+ return idx;
+ }
+
+ private void readHistoryTag(int index, HistoryTag tag) {
+ tag.string = mReadHistoryStrings[index];
+ tag.uid = mReadHistoryUids[index];
+ tag.poolIdx = index;
+ }
+
+ // Part of initial delta int that specifies the time delta.
+ static final int DELTA_TIME_MASK = 0x7ffff;
+ static final int DELTA_TIME_LONG = 0x7ffff; // The delta is a following long
+ static final int DELTA_TIME_INT = 0x7fffe; // The delta is a following int
+ static final int DELTA_TIME_ABS = 0x7fffd; // Following is an entire abs update.
+ // Flag in delta int: a new battery level int follows.
+ static final int DELTA_BATTERY_LEVEL_FLAG = 0x00080000;
+ // Flag in delta int: a new full state and battery status int follows.
+ static final int DELTA_STATE_FLAG = 0x00100000;
+ // Flag in delta int: a new full state2 int follows.
+ static final int DELTA_STATE2_FLAG = 0x00200000;
+ // Flag in delta int: contains a wakelock or wakeReason tag.
+ static final int DELTA_WAKELOCK_FLAG = 0x00400000;
+ // Flag in delta int: contains an event description.
+ static final int DELTA_EVENT_FLAG = 0x00800000;
+ // These upper bits are the frequently changing state bits.
+ static final int DELTA_STATE_MASK = 0xff000000;
+
+ // These are the pieces of battery state that are packed in to the upper bits of
+ // the state int that have been packed in to the first delta int. They must fit
+ // in DELTA_STATE_MASK.
+ static final int STATE_BATTERY_STATUS_MASK = 0x00000007;
+ static final int STATE_BATTERY_STATUS_SHIFT = 29;
+ static final int STATE_BATTERY_HEALTH_MASK = 0x00000007;
+ static final int STATE_BATTERY_HEALTH_SHIFT = 26;
+ static final int STATE_BATTERY_PLUG_MASK = 0x00000003;
+ static final int STATE_BATTERY_PLUG_SHIFT = 24;
+
+ public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
+ if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
+ dest.writeInt(DELTA_TIME_ABS);
+ cur.writeToParcel(dest, 0);
+ return;
+ }
+
+ final long deltaTime = cur.time - last.time;
+ final int lastBatteryLevelInt = buildBatteryLevelInt(last);
+ final int lastStateInt = buildStateInt(last);
+
+ int deltaTimeToken;
+ if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) {
+ deltaTimeToken = DELTA_TIME_LONG;
+ } else if (deltaTime >= DELTA_TIME_ABS) {
+ deltaTimeToken = DELTA_TIME_INT;
+ } else {
+ deltaTimeToken = (int)deltaTime;
+ }
+ int firstToken = deltaTimeToken | (cur.states&DELTA_STATE_MASK);
+ final int batteryLevelInt = buildBatteryLevelInt(cur);
+ final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
+ if (batteryLevelIntChanged) {
+ firstToken |= DELTA_BATTERY_LEVEL_FLAG;
+ }
+ final int stateInt = buildStateInt(cur);
+ final boolean stateIntChanged = stateInt != lastStateInt;
+ if (stateIntChanged) {
+ firstToken |= DELTA_STATE_FLAG;
+ }
+ final boolean state2IntChanged = cur.states2 != last.states2;
+ if (state2IntChanged) {
+ firstToken |= DELTA_STATE2_FLAG;
+ }
+ if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
+ firstToken |= DELTA_WAKELOCK_FLAG;
+ }
+ if (cur.eventCode != HistoryItem.EVENT_NONE) {
+ firstToken |= DELTA_EVENT_FLAG;
+ }
+ dest.writeInt(firstToken);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+ + " deltaTime=" + deltaTime);
- void addHistoryBufferLocked(long curTime) {
+ if (deltaTimeToken >= DELTA_TIME_INT) {
+ if (deltaTimeToken == DELTA_TIME_INT) {
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int)deltaTime);
+ dest.writeInt((int)deltaTime);
+ } else {
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime);
+ dest.writeLong(deltaTime);
+ }
+ }
+ if (batteryLevelIntChanged) {
+ dest.writeInt(batteryLevelInt);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryToken=0x"
+ + Integer.toHexString(batteryLevelInt)
+ + " batteryLevel=" + cur.batteryLevel
+ + " batteryTemp=" + cur.batteryTemperature
+ + " batteryVolt=" + (int)cur.batteryVoltage);
+ }
+ if (stateIntChanged) {
+ dest.writeInt(stateInt);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: stateToken=0x"
+ + Integer.toHexString(stateInt)
+ + " batteryStatus=" + cur.batteryStatus
+ + " batteryHealth=" + cur.batteryHealth
+ + " batteryPlugType=" + cur.batteryPlugType
+ + " states=0x" + Integer.toHexString(cur.states));
+ }
+ if (state2IntChanged) {
+ dest.writeInt(cur.states2);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: states2=0x"
+ + Integer.toHexString(cur.states2));
+ }
+ if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
+ int wakeLockIndex;
+ int wakeReasonIndex;
+ if (cur.wakelockTag != null) {
+ wakeLockIndex = writeHistoryTag(cur.wakelockTag);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+ + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+ } else {
+ wakeLockIndex = 0xffff;
+ }
+ if (cur.wakeReasonTag != null) {
+ wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+ + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+ } else {
+ wakeReasonIndex = 0xffff;
+ }
+ dest.writeInt((wakeReasonIndex<<16) | wakeLockIndex);
+ }
+ if (cur.eventCode != HistoryItem.EVENT_NONE) {
+ int index = writeHistoryTag(cur.eventTag);
+ int codeAndIndex = (cur.eventCode&0xffff) | (index<<16);
+ dest.writeInt(codeAndIndex);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: event=" + cur.eventCode + " tag=#"
+ + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
+ + cur.eventTag.string);
+ }
+ }
+
+ private int buildBatteryLevelInt(HistoryItem h) {
+ return ((((int)h.batteryLevel)<<25)&0xfe000000)
+ | ((((int)h.batteryTemperature)<<14)&0x01ffc000)
+ | (((int)h.batteryVoltage)&0x00003fff);
+ }
+
+ private int buildStateInt(HistoryItem h) {
+ int plugType = 0;
+ if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_AC) != 0) {
+ plugType = 1;
+ } else if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_USB) != 0) {
+ plugType = 2;
+ } else if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0) {
+ plugType = 3;
+ }
+ return ((h.batteryStatus&STATE_BATTERY_STATUS_MASK)<<STATE_BATTERY_STATUS_SHIFT)
+ | ((h.batteryHealth&STATE_BATTERY_HEALTH_MASK)<<STATE_BATTERY_HEALTH_SHIFT)
+ | ((plugType&STATE_BATTERY_PLUG_MASK)<<STATE_BATTERY_PLUG_SHIFT)
+ | (h.states&(~DELTA_STATE_MASK));
+ }
+
+ public void readHistoryDelta(Parcel src, HistoryItem cur) {
+ int firstToken = src.readInt();
+ int deltaTimeToken = firstToken&DELTA_TIME_MASK;
+ cur.cmd = HistoryItem.CMD_UPDATE;
+ cur.numReadInts = 1;
+ if (DEBUG) Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+ + " deltaTimeToken=" + deltaTimeToken);
+
+ if (deltaTimeToken < DELTA_TIME_ABS) {
+ cur.time += deltaTimeToken;
+ } else if (deltaTimeToken == DELTA_TIME_ABS) {
+ cur.time = src.readLong();
+ cur.numReadInts += 2;
+ if (DEBUG) Slog.i(TAG, "READ DELTA: ABS time=" + cur.time);
+ cur.readFromParcel(src);
+ return;
+ } else if (deltaTimeToken == DELTA_TIME_INT) {
+ int delta = src.readInt();
+ cur.time += delta;
+ cur.numReadInts += 1;
+ if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
+ } else {
+ long delta = src.readLong();
+ if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time);
+ cur.time += delta;
+ cur.numReadInts += 2;
+ }
+
+ if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) {
+ int batteryLevelInt = src.readInt();
+ cur.batteryLevel = (byte)((batteryLevelInt>>25)&0x7f);
+ cur.batteryTemperature = (short)((batteryLevelInt<<7)>>21);
+ cur.batteryVoltage = (char)(batteryLevelInt&0x3fff);
+ cur.numReadInts += 1;
+ if (DEBUG) Slog.i(TAG, "READ DELTA: batteryToken=0x"
+ + Integer.toHexString(batteryLevelInt)
+ + " batteryLevel=" + cur.batteryLevel
+ + " batteryTemp=" + cur.batteryTemperature
+ + " batteryVolt=" + (int)cur.batteryVoltage);
+ }
+
+ if ((firstToken&DELTA_STATE_FLAG) != 0) {
+ int stateInt = src.readInt();
+ cur.states = (firstToken&DELTA_STATE_MASK) | (stateInt&(~DELTA_STATE_MASK));
+ cur.batteryStatus = (byte)((stateInt>>STATE_BATTERY_STATUS_SHIFT)
+ & STATE_BATTERY_STATUS_MASK);
+ cur.batteryHealth = (byte)((stateInt>>STATE_BATTERY_HEALTH_SHIFT)
+ & STATE_BATTERY_HEALTH_MASK);
+ cur.batteryPlugType = (byte)((stateInt>>STATE_BATTERY_PLUG_SHIFT)
+ & STATE_BATTERY_PLUG_MASK);
+ switch (cur.batteryPlugType) {
+ case 1:
+ cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_AC;
+ break;
+ case 2:
+ cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_USB;
+ break;
+ case 3:
+ cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ break;
+ }
+ cur.numReadInts += 1;
+ if (DEBUG) Slog.i(TAG, "READ DELTA: stateToken=0x"
+ + Integer.toHexString(stateInt)
+ + " batteryStatus=" + cur.batteryStatus
+ + " batteryHealth=" + cur.batteryHealth
+ + " batteryPlugType=" + cur.batteryPlugType
+ + " states=0x" + Integer.toHexString(cur.states));
+ } else {
+ cur.states = (firstToken&DELTA_STATE_MASK) | (cur.states&(~DELTA_STATE_MASK));
+ }
+
+ if ((firstToken&DELTA_STATE2_FLAG) != 0) {
+ cur.states2 = src.readInt();
+ if (DEBUG) Slog.i(TAG, "READ DELTA: states2=0x"
+ + Integer.toHexString(cur.states2));
+ }
+
+ if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) {
+ int indexes = src.readInt();
+ int wakeLockIndex = indexes&0xffff;
+ int wakeReasonIndex = (indexes>>16)&0xffff;
+ if (wakeLockIndex != 0xffff) {
+ cur.wakelockTag = cur.localWakelockTag;
+ readHistoryTag(wakeLockIndex, cur.wakelockTag);
+ if (DEBUG) Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+ + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+ } else {
+ cur.wakelockTag = null;
+ }
+ if (wakeReasonIndex != 0xffff) {
+ cur.wakeReasonTag = cur.localWakeReasonTag;
+ readHistoryTag(wakeReasonIndex, cur.wakeReasonTag);
+ if (DEBUG) Slog.i(TAG, "READ DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+ + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+ } else {
+ cur.wakeReasonTag = null;
+ }
+ cur.numReadInts += 1;
+ } else {
+ cur.wakelockTag = null;
+ cur.wakeReasonTag = null;
+ }
+
+ if ((firstToken&DELTA_EVENT_FLAG) != 0) {
+ cur.eventTag = cur.localEventTag;
+ final int codeAndIndex = src.readInt();
+ cur.eventCode = (codeAndIndex&0xffff);
+ final int index = ((codeAndIndex>>16)&0xffff);
+ readHistoryTag(index, cur.eventTag);
+ cur.numReadInts += 1;
+ if (DEBUG) Slog.i(TAG, "READ DELTA: event=" + cur.eventCode + " tag=#"
+ + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
+ + cur.eventTag.string);
+ } else {
+ cur.eventCode = HistoryItem.EVENT_NONE;
+ }
+ }
+
+ void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
- final long timeDiff = (mHistoryBaseTime+curTime) - mHistoryLastWritten.time;
+ final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
+ final int diffStates = mHistoryLastWritten.states^cur.states;
+ final int diffStates2 = mHistoryLastWritten.states2^cur.states2;
+ final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
+ final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
+ if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
+ + Integer.toHexString(diffStates) + " lastDiff="
+ + Integer.toHexString(lastDiffStates) + " diff2="
+ + Integer.toHexString(diffStates2) + " lastDiff2="
+ + Integer.toHexString(lastDiffStates2));
if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
- && timeDiff < 2000
- && ((mHistoryLastWritten.states^mHistoryCur.states)&mChangedBufferStates) == 0) {
- // If the current is the same as the one before, then we no
- // longer need the entry.
+ && timeDiff < 1000 && (diffStates&lastDiffStates) == 0
+ && (diffStates2&lastDiffStates2) == 0
+ && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
+ && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
+ && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
+ || cur.eventCode == HistoryItem.EVENT_NONE)
+ && mHistoryLastWritten.batteryLevel == cur.batteryLevel
+ && mHistoryLastWritten.batteryStatus == cur.batteryStatus
+ && mHistoryLastWritten.batteryHealth == cur.batteryHealth
+ && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
+ && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
+ && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
+ // We can merge this new change in with the last one. Merging is
+ // allowed as long as only the states have changed, and within those states
+ // as long as no bit has changed both between now and the last entry, as
+ // well as the last entry and the one before it (so we capture any toggles).
+ if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos);
mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
mHistoryBufferLastPos = -1;
- if (mHistoryLastLastWritten.cmd == HistoryItem.CMD_UPDATE
- && timeDiff < 500 && mHistoryLastLastWritten.same(mHistoryCur)) {
- // If this results in us returning to the state written
- // prior to the last one, then we can just delete the last
- // written one and drop the new one. Nothing more to do.
- mHistoryLastWritten.setTo(mHistoryLastLastWritten);
- mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
- return;
+ elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTime;
+ // If the last written history had a wakelock tag, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have a wakelock tag.
+ if (mHistoryLastWritten.wakelockTag != null) {
+ cur.wakelockTag = cur.localWakelockTag;
+ cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
+ }
+ // If the last written history had a wake reason tag, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have a wakelock tag.
+ if (mHistoryLastWritten.wakeReasonTag != null) {
+ cur.wakeReasonTag = cur.localWakeReasonTag;
+ cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
+ }
+ // If the last written history had an event, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have an event.
+ if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
+ cur.eventCode = mHistoryLastWritten.eventCode;
+ cur.eventTag = cur.localEventTag;
+ cur.eventTag.setTo(mHistoryLastWritten.eventTag);
}
- mChangedBufferStates |= mHistoryLastWritten.states^mHistoryCur.states;
- curTime = mHistoryLastWritten.time - mHistoryBaseTime;
mHistoryLastWritten.setTo(mHistoryLastLastWritten);
- } else {
- mChangedBufferStates = 0;
}
final int dataSize = mHistoryBuffer.dataSize();
if (dataSize >= MAX_HISTORY_BUFFER) {
if (!mHistoryOverflow) {
mHistoryOverflow = true;
- addHistoryBufferLocked(curTime, HistoryItem.CMD_OVERFLOW);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
+ return;
}
// Once we've reached the maximum number of items, we only
// record changes to the battery level and the most interesting states.
// Once we've reached the maximum maximum number of items, we only
// record changes to the battery level.
- if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
+ if (mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
(dataSize >= MAX_MAX_HISTORY_BUFFER
- || ((mHistoryLastWritten.states^mHistoryCur.states)
+ || ((mHistoryLastWritten.states^cur.states)
& HistoryItem.MOST_INTERESTING_STATES) == 0)) {
return;
}
+
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+ return;
}
- addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
}
- void addHistoryBufferLocked(long curTime, byte cmd) {
- int origPos = 0;
+ private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd,
+ HistoryItem cur) {
if (mIteratingHistory) {
- origPos = mHistoryBuffer.dataPosition();
- mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+ throw new IllegalStateException("Can't do this while iterating history!");
}
mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
mHistoryLastLastWritten.setTo(mHistoryLastWritten);
- mHistoryLastWritten.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
- mHistoryLastWritten.writeDelta(mHistoryBuffer, mHistoryLastLastWritten);
- mLastHistoryTime = curTime;
+ mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
+ writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
+ mLastHistoryElapsedRealtime = elapsedRealtimeMs;
+ cur.wakelockTag = null;
+ cur.wakeReasonTag = null;
+ cur.eventCode = HistoryItem.EVENT_NONE;
+ cur.eventTag = null;
if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+ " now " + mHistoryBuffer.dataPosition()
+ " size is now " + mHistoryBuffer.dataSize());
- if (mIteratingHistory) {
- mHistoryBuffer.setDataPosition(origPos);
- }
}
int mChangedStates = 0;
+ int mChangedStates2 = 0;
+
+ void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
+ if (mTrackRunningHistoryElapsedRealtime != 0) {
+ final long diffElapsed = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtime;
+ final long diffUptime = uptimeMs - mTrackRunningHistoryUptime;
+ if (diffUptime < (diffElapsed-20)) {
+ final long wakeElapsedTime = elapsedRealtimeMs - (diffElapsed - diffUptime);
+ mHistoryAddTmp.setTo(mHistoryLastWritten);
+ mHistoryAddTmp.wakelockTag = null;
+ mHistoryAddTmp.wakeReasonTag = null;
+ mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
+ mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
+ addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp);
+ }
+ }
+ mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
+ mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
+ mTrackRunningHistoryUptime = uptimeMs;
+ addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+ }
- void addHistoryRecordLocked(long curTime) {
- addHistoryBufferLocked(curTime);
+ void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
if (!USE_OLD_HISTORY) {
return;
@@ -1567,30 +2153,33 @@ public final class BatteryStatsImpl extends BatteryStats {
// are now resetting back to their original value, then just collapse
// into one record.
if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
- && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+2000)
- && ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 0) {
+ && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
+ && ((mHistoryEnd.states^cur.states)&mChangedStates) == 0
+ && ((mHistoryEnd.states2^cur.states2)&mChangedStates2) == 0) {
// If the current is the same as the one before, then we no
// longer need the entry.
if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
- && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)
- && mHistoryLastEnd.same(mHistoryCur)) {
+ && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+500)
+ && mHistoryLastEnd.sameNonEvent(cur)) {
mHistoryLastEnd.next = null;
mHistoryEnd.next = mHistoryCache;
mHistoryCache = mHistoryEnd;
mHistoryEnd = mHistoryLastEnd;
mHistoryLastEnd = null;
} else {
- mChangedStates |= mHistoryEnd.states^mHistoryCur.states;
- mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
+ mChangedStates |= mHistoryEnd.states^cur.states;
+ mChangedStates2 |= mHistoryEnd.states^cur.states2;
+ mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, cur);
}
return;
}
mChangedStates = 0;
+ mChangedStates2 = 0;
if (mNumHistoryItems == MAX_HISTORY_ITEMS
|| mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
- addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW);
+ addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW);
}
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
@@ -1599,25 +2188,34 @@ public final class BatteryStatsImpl extends BatteryStats {
// Once we've reached the maximum maximum number of items, we only
// record changes to the battery level.
if (mHistoryEnd != null && mHistoryEnd.batteryLevel
- == mHistoryCur.batteryLevel &&
+ == cur.batteryLevel &&
(mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
- || ((mHistoryEnd.states^mHistoryCur.states)
+ || ((mHistoryEnd.states^cur.states)
& HistoryItem.MOST_INTERESTING_STATES) == 0)) {
return;
}
}
- addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
+ addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE);
+ }
+
+ void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
+ String name, int uid) {
+ mHistoryCur.eventCode = code;
+ mHistoryCur.eventTag = mHistoryCur.localEventTag;
+ mHistoryCur.eventTag.string = name;
+ mHistoryCur.eventTag.uid = uid;
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
- void addHistoryRecordLocked(long curTime, byte cmd) {
+ void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd, HistoryItem cur) {
HistoryItem rec = mHistoryCache;
if (rec != null) {
mHistoryCache = rec.next;
} else {
rec = new HistoryItem();
}
- rec.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+ rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
addHistoryRecordLocked(rec);
}
@@ -1646,114 +2244,247 @@ public final class BatteryStatsImpl extends BatteryStats {
}
mHistoryBaseTime = 0;
- mLastHistoryTime = 0;
+ mLastHistoryElapsedRealtime = 0;
+ mTrackRunningHistoryElapsedRealtime = 0;
+ mTrackRunningHistoryUptime = 0;
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
- mHistoryBuffer.setDataCapacity(MAX_HISTORY_BUFFER/2);
- mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
- mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
+ mHistoryBuffer.setDataCapacity(MAX_HISTORY_BUFFER / 2);
+ mHistoryLastLastWritten.clear();
+ mHistoryLastWritten.clear();
+ mHistoryTagPool.clear();
+ mNextHistoryTagIdx = 0;
+ mNumHistoryTagChars = 0;
mHistoryBufferLastPos = -1;
mHistoryOverflow = false;
}
- public void doUnplugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
- for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
- mUnpluggables.get(i).unplug(elapsedRealtime, batteryUptime, batteryRealtime);
+ public void updateTimeBasesLocked(boolean unplugged, boolean screenOff, long uptime,
+ long realtime) {
+ if (mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime)) {
+ if (unplugged) {
+ // Track bt headset ping count
+ mBluetoothPingStart = getCurrentBluetoothPingCount();
+ mBluetoothPingCount = 0;
+ } else {
+ // Track bt headset ping count
+ mBluetoothPingCount = getBluetoothPingCount();
+ mBluetoothPingStart = -1;
+ }
+ }
+
+ boolean unpluggedScreenOff = unplugged && screenOff;
+ if (unpluggedScreenOff != mOnBatteryScreenOffTimeBase.isRunning()) {
+ updateKernelWakelocksLocked();
+ requestWakelockCpuUpdate();
+ if (!unpluggedScreenOff) {
+ // We are switching to no longer tracking wake locks, but we want
+ // the next CPU update we receive to take them in to account.
+ mDistributeWakelockCpu = true;
+ }
+ mOnBatteryScreenOffTimeBase.setRunning(unpluggedScreenOff, uptime, realtime);
+ }
+ }
+
+ public void addIsolatedUidLocked(int isolatedUid, int appUid) {
+ mIsolatedUids.put(isolatedUid, appUid);
+ }
+
+ public void removeIsolatedUidLocked(int isolatedUid, int appUid) {
+ int curUid = mIsolatedUids.get(isolatedUid, -1);
+ if (curUid == appUid) {
+ mIsolatedUids.delete(isolatedUid);
}
+ }
- // Track radio awake time
- mRadioDataStart = getCurrentRadioDataUptime();
- mRadioDataUptime = 0;
+ public int mapUid(int uid) {
+ int isolated = mIsolatedUids.get(uid, -1);
+ return isolated > 0 ? isolated : uid;
+ }
- // Track bt headset ping count
- mBluetoothPingStart = getCurrentBluetoothPingCount();
- mBluetoothPingCount = 0;
+ public void noteEventLocked(int code, String name, int uid) {
+ uid = mapUid(uid);
+ if (!mActiveEvents.updateState(code, name, uid, 0)) {
+ return;
+ }
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid);
}
- public void doPlugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
- for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
- mUnpluggables.get(i).plug(elapsedRealtime, batteryUptime, batteryRealtime);
+ private void requestWakelockCpuUpdate() {
+ if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
+ Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
+ mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS);
}
+ }
- // Track radio awake time
- mRadioDataUptime = getRadioDataUptime();
- mRadioDataStart = -1;
+ public void setRecordAllWakeLocksLocked(boolean enabled) {
+ mRecordAllWakeLocks = enabled;
+ if (!enabled) {
+ // Clear out any existing state.
+ mActiveEvents.removeEvents(HistoryItem.EVENT_WAKE_LOCK);
+ }
+ }
- // Track bt headset ping count
- mBluetoothPingCount = getBluetoothPingCount();
- mBluetoothPingStart = -1;
+ public void setNoAutoReset(boolean enabled) {
+ mNoAutoReset = enabled;
}
- int mWakeLockNesting;
+ private String mInitialAcquireWakeName;
+ private int mInitialAcquireWakeUid = -1;
- public void noteStartWakeLocked(int uid, int pid, String name, int type) {
+ public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type,
+ boolean unimportantForLogging, long elapsedRealtime, long uptime) {
+ uid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
// Only care about partial wake locks, since full wake locks
// will be canceled when the user puts the screen to sleep.
+ aggregateLastWakeupUptimeLocked(uptime);
+ if (mRecordAllWakeLocks) {
+ if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, name, uid, 0)) {
+ addHistoryEventLocked(elapsedRealtime, uptime,
+ HistoryItem.EVENT_WAKE_LOCK_START, name, uid);
+ }
+ }
+ historyName = historyName == null ? name : historyName;
if (mWakeLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+ mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
+ mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
+ mWakeLockImportant = !unimportantForLogging;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ } else if (!mWakeLockImportant && !unimportantForLogging) {
+ if (mHistoryLastWritten.wakelockTag != null) {
+ // We'll try to update the last tag.
+ mHistoryLastWritten.wakelockTag = null;
+ mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+ mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
+ mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+ mWakeLockImportant = true;
}
mWakeLockNesting++;
}
if (uid >= 0) {
- if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
- Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
- mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS);
- }
- getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type);
+ //if (uid == 0) {
+ // Slog.wtf(TAG, "Acquiring wake lock from root: " + name);
+ //}
+ requestWakelockCpuUpdate();
+ getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime);
}
}
- public void noteStopWakeLocked(int uid, int pid, String name, int type) {
+ public void noteStopWakeLocked(int uid, int pid, String name, String historyName, int type,
+ long elapsedRealtime, long uptime) {
+ uid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
mWakeLockNesting--;
+ if (mRecordAllWakeLocks) {
+ if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid, 0)) {
+ addHistoryEventLocked(elapsedRealtime, uptime,
+ HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid);
+ }
+ }
if (mWakeLockNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ mInitialAcquireWakeName = null;
+ mInitialAcquireWakeUid = -1;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
if (uid >= 0) {
- if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
- Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
- mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS);
- }
- getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type);
+ requestWakelockCpuUpdate();
+ getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
}
}
- public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
- int N = ws.size();
+ public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
+ String historyName, int type, boolean unimportantForLogging) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ final int N = ws.size();
for (int i=0; i<N; i++) {
- noteStartWakeLocked(ws.get(i), pid, name, type);
+ noteStartWakeLocked(ws.get(i), pid, name, historyName, type, unimportantForLogging,
+ elapsedRealtime, uptime);
}
}
- public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
- int N = ws.size();
+ public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name,
+ String historyName, int type, WorkSource newWs, int newPid, String newName,
+ String newHistoryName, int newType, boolean newUnimportantForLogging) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ // For correct semantics, we start the need worksources first, so that we won't
+ // make inappropriate history items as if all wake locks went away and new ones
+ // appeared. This is okay because tracking of wake locks allows nesting.
+ final int NN = newWs.size();
+ for (int i=0; i<NN; i++) {
+ noteStartWakeLocked(newWs.get(i), newPid, newName, newHistoryName, newType,
+ newUnimportantForLogging, elapsedRealtime, uptime);
+ }
+ final int NO = ws.size();
+ for (int i=0; i<NO; i++) {
+ noteStopWakeLocked(ws.get(i), pid, name, historyName, type, elapsedRealtime, uptime);
+ }
+ }
+
+ public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name,
+ String historyName, int type) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ final int N = ws.size();
for (int i=0; i<N; i++) {
- noteStopWakeLocked(ws.get(i), pid, name, type);
+ noteStopWakeLocked(ws.get(i), pid, name, historyName, type, elapsedRealtime, uptime);
+ }
+ }
+
+ void aggregateLastWakeupUptimeLocked(long uptimeMs) {
+ if (mLastWakeupReason != null) {
+ long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
+ LongSamplingCounter timer = getWakeupReasonCounterLocked(mLastWakeupReason);
+ timer.addCountLocked(deltaUptime);
+ mLastWakeupReason = null;
}
}
+ public void noteWakeupReasonLocked(String reason) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason reason \"" + reason +"\": "
+ + Integer.toHexString(mHistoryCur.states));
+ aggregateLastWakeupUptimeLocked(uptime);
+ mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
+ mHistoryCur.wakeReasonTag.string = reason;
+ mHistoryCur.wakeReasonTag.uid = 0;
+ mLastWakeupReason = reason;
+ mLastWakeupUptimeMs = uptime;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+
public int startAddingCpuLocked() {
mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
- if (mScreenState == Display.STATE_ON) {
- return 0;
- }
-
final int N = mPartialTimers.size();
if (N == 0) {
mLastPartialTimers.clear();
+ mDistributeWakelockCpu = false;
+ return 0;
+ }
+
+ if (!mOnBatteryScreenOffTimeBase.isRunning() && !mDistributeWakelockCpu) {
return 0;
}
+ mDistributeWakelockCpu = false;
+
// How many timers should consume CPU? Only want to include ones
// that have already been in the list.
for (int i=0; i<N; i++) {
@@ -1840,6 +2571,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
public void noteProcessDiedLocked(int uid, int pid) {
+ uid = mapUid(uid);
Uid u = mUidStats.get(uid);
if (u != null) {
u.mPids.remove(pid);
@@ -1847,17 +2579,19 @@ public final class BatteryStatsImpl extends BatteryStats {
}
public long getProcessWakeTime(int uid, int pid, long realtime) {
+ uid = mapUid(uid);
Uid u = mUidStats.get(uid);
if (u != null) {
Uid.Pid p = u.mPids.get(pid);
if (p != null) {
- return p.mWakeSum + (p.mWakeStart != 0 ? (realtime - p.mWakeStart) : 0);
+ return p.mWakeSumMs + (p.mWakeNesting > 0 ? (realtime - p.mWakeStartMs) : 0);
}
}
return 0;
}
public void reportExcessiveWakeLocked(int uid, String proc, long overTime, long usedTime) {
+ uid = mapUid(uid);
Uid u = mUidStats.get(uid);
if (u != null) {
u.reportExcessiveWakeLocked(proc, overTime, usedTime);
@@ -1865,6 +2599,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
public void reportExcessiveCpuLocked(int uid, String proc, long overTime, long usedTime) {
+ uid = mapUid(uid);
Uid u = mUidStats.get(uid);
if (u != null) {
u.reportExcessiveCpuLocked(proc, overTime, usedTime);
@@ -1874,49 +2609,61 @@ public final class BatteryStatsImpl extends BatteryStats {
int mSensorNesting;
public void noteStartSensorLocked(int uid, int sensor) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mSensorNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mSensorNesting++;
- getUidStatsLocked(uid).noteStartSensor(sensor);
+ getUidStatsLocked(uid).noteStartSensor(sensor, elapsedRealtime);
}
public void noteStopSensorLocked(int uid, int sensor) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mSensorNesting--;
if (mSensorNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
- getUidStatsLocked(uid).noteStopSensor(sensor);
+ getUidStatsLocked(uid).noteStopSensor(sensor, elapsedRealtime);
}
int mGpsNesting;
public void noteStartGpsLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mGpsNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mGpsNesting++;
- getUidStatsLocked(uid).noteStartGps();
+ getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
}
public void noteStopGpsLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mGpsNesting--;
if (mGpsNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
- getUidStatsLocked(uid).noteStopGps();
+ getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
}
public void noteScreenStateLocked(int state) {
@@ -1928,18 +2675,24 @@ public final class BatteryStatsImpl extends BatteryStats {
if (state == Display.STATE_ON) {
// Screen turning on.
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
- mScreenOnTimer.startRunningLocked(this);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mScreenOnTimer.startRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
- mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this);
+ mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(elapsedRealtime);
}
+ updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), false,
+ SystemClock.uptimeMillis() * 1000, elapsedRealtime * 1000);
+
// Fake a wake lock, so we consider the device waked as long
// as the screen is on.
- noteStartWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
+ noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
+ elapsedRealtime, uptime);
// Update discharge amounts.
if (mOnBatteryInternal) {
@@ -1947,16 +2700,22 @@ public final class BatteryStatsImpl extends BatteryStats {
}
} else if (oldState == Display.STATE_ON) {
// Screen turning off or dozing.
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
- mScreenOnTimer.stopRunningLocked(this);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mScreenOnTimer.stopRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
- mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
+ mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
}
- noteStopWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
+ noteStopWakeLocked(-1, -1, "screen", "screen", WAKE_TYPE_PARTIAL,
+ elapsedRealtime, uptime);
+
+ updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true,
+ SystemClock.uptimeMillis() * 1000, elapsedRealtime * 1000);
// Update discharge amounts.
if (mOnBatteryInternal) {
@@ -1972,66 +2731,136 @@ public final class BatteryStatsImpl extends BatteryStats {
if (bin < 0) bin = 0;
else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
if (mScreenBrightnessBin != bin) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_BRIGHTNESS_MASK)
| (bin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
if (mScreenState == Display.STATE_ON) {
if (mScreenBrightnessBin >= 0) {
- mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
+ mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
}
- mScreenBrightnessTimer[bin].startRunningLocked(this);
+ mScreenBrightnessTimer[bin].startRunningLocked(elapsedRealtime);
}
mScreenBrightnessBin = bin;
}
}
public void noteUserActivityLocked(int uid, int event) {
- getUidStatsLocked(uid).noteUserActivityLocked(event);
+ if (mOnBatteryInternal) {
+ uid = mapUid(uid);
+ getUidStatsLocked(uid).noteUserActivityLocked(event);
+ }
}
public void noteInteractiveLocked(boolean interactive) {
if (mInteractive != interactive) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
mInteractive = interactive;
if (DEBUG) Slog.v(TAG, "Interactive: " + interactive);
if (interactive) {
- mInteractiveTimer.startRunningLocked(this);
+ mInteractiveTimer.startRunningLocked(elapsedRealtime);
} else {
- mInteractiveTimer.stopRunningLocked(this);
+ mInteractiveTimer.stopRunningLocked(elapsedRealtime);
}
}
}
+ public void noteMobileRadioPowerState(int powerState, long timestampNs) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ if (mMobileRadioPowerState != powerState) {
+ long realElapsedRealtimeMs;
+ final boolean active =
+ powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
+ || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
+ if (active) {
+ realElapsedRealtimeMs = elapsedRealtime;
+ mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+ } else {
+ realElapsedRealtimeMs = timestampNs / (1000*1000);
+ long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
+ if (realElapsedRealtimeMs < lastUpdateTimeMs) {
+ Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
+ + " is before start time " + lastUpdateTimeMs);
+ realElapsedRealtimeMs = elapsedRealtime;
+ } else if (realElapsedRealtimeMs < elapsedRealtime) {
+ mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtime
+ - realElapsedRealtimeMs);
+ }
+ mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+ }
+ if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mMobileRadioPowerState = powerState;
+ if (active) {
+ mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
+ mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
+ } else {
+ mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
+ updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
+ mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
+ }
+ }
+ }
+
+ public void noteLowPowerMode(boolean enabled) {
+ if (mLowPowerModeEnabled != enabled) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ mLowPowerModeEnabled = enabled;
+ if (enabled) {
+ mHistoryCur.states2 |= HistoryItem.STATE2_LOW_POWER_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Low power mode enabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ mLowPowerModeEnabledTimer.startRunningLocked(elapsedRealtime);
+ } else {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_LOW_POWER_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Low power mode disabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ mLowPowerModeEnabledTimer.stopRunningLocked(elapsedRealtime);
+ }
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+ }
+
public void notePhoneOnLocked() {
if (!mPhoneOn) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mPhoneOn = true;
- mPhoneOnTimer.startRunningLocked(this);
+ mPhoneOnTimer.startRunningLocked(elapsedRealtime);
}
}
public void notePhoneOffLocked() {
if (mPhoneOn) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mPhoneOn = false;
- mPhoneOnTimer.stopRunningLocked(this);
+ mPhoneOnTimer.stopRunningLocked(elapsedRealtime);
}
}
void stopAllSignalStrengthTimersLocked(int except) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
if (i == except) {
continue;
}
while (mPhoneSignalStrengthsTimer[i].isRunningLocked()) {
- mPhoneSignalStrengthsTimer[i].stopRunningLocked(this);
+ mPhoneSignalStrengthsTimer[i].stopRunningLocked(elapsedRealtime);
}
}
}
@@ -2049,26 +2878,29 @@ public final class BatteryStatsImpl extends BatteryStats {
return state;
}
- private void updateAllPhoneStateLocked(int state, int simState, int bin) {
+ private void updateAllPhoneStateLocked(int state, int simState, int strengthBin) {
boolean scanning = false;
boolean newHistory = false;
mPhoneServiceStateRaw = state;
mPhoneSimStateRaw = simState;
- mPhoneSignalStrengthBinRaw = bin;
+ mPhoneSignalStrengthBinRaw = strengthBin;
+
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (simState == TelephonyManager.SIM_STATE_ABSENT) {
// In this case we will always be STATE_OUT_OF_SERVICE, so need
// to infer that we are scanning from other data.
if (state == ServiceState.STATE_OUT_OF_SERVICE
- && bin > SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ && strengthBin > SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
state = ServiceState.STATE_IN_SERVICE;
}
}
// If the phone is powered off, stop all timers.
if (state == ServiceState.STATE_POWER_OFF) {
- bin = -1;
+ strengthBin = -1;
// If we are in service, make sure the correct signal string timer is running.
} else if (state == ServiceState.STATE_IN_SERVICE) {
@@ -2078,13 +2910,13 @@ public final class BatteryStatsImpl extends BatteryStats {
// bin and have the scanning bit set.
} else if (state == ServiceState.STATE_OUT_OF_SERVICE) {
scanning = true;
- bin = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ strengthBin = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
if (!mPhoneSignalScanningTimer.isRunningLocked()) {
mHistoryCur.states |= HistoryItem.STATE_PHONE_SCANNING_FLAG;
newHistory = true;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone started scanning to: "
+ Integer.toHexString(mHistoryCur.states));
- mPhoneSignalScanningTimer.startRunningLocked(this);
+ mPhoneSignalScanningTimer.startRunningLocked(elapsedRealtime);
}
}
@@ -2095,7 +2927,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "Phone stopped scanning to: "
+ Integer.toHexString(mHistoryCur.states));
newHistory = true;
- mPhoneSignalScanningTimer.stopRunningLocked(this);
+ mPhoneSignalScanningTimer.stopRunningLocked(elapsedRealtime);
}
}
@@ -2108,27 +2940,28 @@ public final class BatteryStatsImpl extends BatteryStats {
mPhoneServiceState = state;
}
- if (mPhoneSignalStrengthBin != bin) {
+ if (mPhoneSignalStrengthBin != strengthBin) {
if (mPhoneSignalStrengthBin >= 0) {
- mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(this);
+ mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(
+ elapsedRealtime);
}
- if (bin >= 0) {
- if (!mPhoneSignalStrengthsTimer[bin].isRunningLocked()) {
- mPhoneSignalStrengthsTimer[bin].startRunningLocked(this);
+ if (strengthBin >= 0) {
+ if (!mPhoneSignalStrengthsTimer[strengthBin].isRunningLocked()) {
+ mPhoneSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtime);
}
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_SIGNAL_STRENGTH_MASK)
- | (bin << HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT);
- if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + bin + " to: "
+ | (strengthBin << HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + strengthBin + " to: "
+ Integer.toHexString(mHistoryCur.states));
newHistory = true;
} else {
stopAllSignalStrengthTimersLocked(-1);
}
- mPhoneSignalStrengthBin = bin;
+ mPhoneSignalStrengthBin = strengthBin;
}
if (newHistory) {
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
@@ -2202,120 +3035,142 @@ public final class BatteryStatsImpl extends BatteryStats {
}
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
| (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
if (mPhoneDataConnectionType >= 0) {
- mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this);
+ mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(
+ elapsedRealtime);
}
mPhoneDataConnectionType = bin;
- mPhoneDataConnectionsTimer[bin].startRunningLocked(this);
+ mPhoneDataConnectionsTimer[bin].startRunningLocked(elapsedRealtime);
}
}
public void noteWifiOnLocked() {
if (!mWifiOn) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_WIFI_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = true;
- mWifiOnTimer.startRunningLocked(this);
+ mWifiOnTimer.startRunningLocked(elapsedRealtime);
}
}
public void noteWifiOffLocked() {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiOn) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = false;
- mWifiOnTimer.stopRunningLocked(this);
- }
- if (mWifiOnUid >= 0) {
- getUidStatsLocked(mWifiOnUid).noteWifiStoppedLocked();
- mWifiOnUid = -1;
+ mWifiOnTimer.stopRunningLocked(elapsedRealtime);
}
}
public void noteAudioOnLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (!mAudioOn) {
mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mAudioOn = true;
- mAudioOnTimer.startRunningLocked(this);
+ mAudioOnTimer.startRunningLocked(elapsedRealtime);
}
- getUidStatsLocked(uid).noteAudioTurnedOnLocked();
+ getUidStatsLocked(uid).noteAudioTurnedOnLocked(elapsedRealtime);
}
public void noteAudioOffLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mAudioOn) {
mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mAudioOn = false;
- mAudioOnTimer.stopRunningLocked(this);
+ mAudioOnTimer.stopRunningLocked(elapsedRealtime);
}
- getUidStatsLocked(uid).noteAudioTurnedOffLocked();
+ getUidStatsLocked(uid).noteAudioTurnedOffLocked(elapsedRealtime);
}
public void noteVideoOnLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (!mVideoOn) {
- mHistoryCur.states |= HistoryItem.STATE_VIDEO_ON_FLAG;
+ mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mVideoOn = true;
- mVideoOnTimer.startRunningLocked(this);
+ mVideoOnTimer.startRunningLocked(elapsedRealtime);
}
- getUidStatsLocked(uid).noteVideoTurnedOnLocked();
+ getUidStatsLocked(uid).noteVideoTurnedOnLocked(elapsedRealtime);
}
public void noteVideoOffLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mVideoOn) {
- mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mVideoOn = false;
- mVideoOnTimer.stopRunningLocked(this);
+ mVideoOnTimer.stopRunningLocked(elapsedRealtime);
}
- getUidStatsLocked(uid).noteVideoTurnedOffLocked();
+ getUidStatsLocked(uid).noteVideoTurnedOffLocked(elapsedRealtime);
}
public void noteActivityResumedLocked(int uid) {
- getUidStatsLocked(uid).noteActivityResumedLocked();
+ uid = mapUid(uid);
+ getUidStatsLocked(uid).noteActivityResumedLocked(SystemClock.elapsedRealtime());
}
public void noteActivityPausedLocked(int uid) {
- getUidStatsLocked(uid).noteActivityPausedLocked();
+ uid = mapUid(uid);
+ getUidStatsLocked(uid).noteActivityPausedLocked(SystemClock.elapsedRealtime());
}
public void noteVibratorOnLocked(int uid, long durationMillis) {
+ uid = mapUid(uid);
getUidStatsLocked(uid).noteVibratorOnLocked(durationMillis);
}
public void noteVibratorOffLocked(int uid) {
+ uid = mapUid(uid);
getUidStatsLocked(uid).noteVibratorOffLocked();
}
public void noteWifiRunningLocked(WorkSource ws) {
if (!mGlobalWifiRunning) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mGlobalWifiRunning = true;
- mGlobalWifiRunningTimer.startRunningLocked(this);
+ mGlobalWifiRunningTimer.startRunningLocked(elapsedRealtime);
int N = ws.size();
for (int i=0; i<N; i++) {
- getUidStatsLocked(ws.get(i)).noteWifiRunningLocked();
+ int uid = mapUid(ws.get(i));
+ getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
}
} else {
Log.w(TAG, "noteWifiRunningLocked -- called while WIFI running");
@@ -2324,13 +3179,16 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiRunningChangedLocked(WorkSource oldWs, WorkSource newWs) {
if (mGlobalWifiRunning) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
int N = oldWs.size();
for (int i=0; i<N; i++) {
- getUidStatsLocked(oldWs.get(i)).noteWifiStoppedLocked();
+ int uid = mapUid(oldWs.get(i));
+ getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
}
N = newWs.size();
for (int i=0; i<N; i++) {
- getUidStatsLocked(newWs.get(i)).noteWifiRunningLocked();
+ int uid = mapUid(newWs.get(i));
+ getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
}
} else {
Log.w(TAG, "noteWifiRunningChangedLocked -- called while WIFI not running");
@@ -2339,121 +3197,174 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiStoppedLocked(WorkSource ws) {
if (mGlobalWifiRunning) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RUNNING_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mGlobalWifiRunning = false;
- mGlobalWifiRunningTimer.stopRunningLocked(this);
+ mGlobalWifiRunningTimer.stopRunningLocked(elapsedRealtime);
int N = ws.size();
for (int i=0; i<N; i++) {
- getUidStatsLocked(ws.get(i)).noteWifiStoppedLocked();
+ int uid = mapUid(ws.get(i));
+ getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
}
} else {
Log.w(TAG, "noteWifiStoppedLocked -- called while WIFI not running");
}
}
+ public void noteWifiStateLocked(int wifiState, String accessPoint) {
+ if (DEBUG) Log.i(TAG, "WiFi state -> " + wifiState);
+ if (mWifiState != wifiState) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ if (mWifiState >= 0) {
+ mWifiStateTimer[mWifiState].stopRunningLocked(elapsedRealtime);
+ }
+ mWifiState = wifiState;
+ mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtime);
+ }
+ }
+
public void noteBluetoothOnLocked() {
if (!mBluetoothOn) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_BLUETOOTH_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = true;
- mBluetoothOnTimer.startRunningLocked(this);
+ mBluetoothOnTimer.startRunningLocked(elapsedRealtime);
}
}
public void noteBluetoothOffLocked() {
if (mBluetoothOn) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_BLUETOOTH_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = false;
- mBluetoothOnTimer.stopRunningLocked(this);
+ mBluetoothOnTimer.stopRunningLocked(elapsedRealtime);
+ }
+ }
+
+ public void noteBluetoothStateLocked(int bluetoothState) {
+ if (DEBUG) Log.i(TAG, "Bluetooth state -> " + bluetoothState);
+ if (mBluetoothState != bluetoothState) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ if (mBluetoothState >= 0) {
+ mBluetoothStateTimer[mBluetoothState].stopRunningLocked(elapsedRealtime);
+ }
+ mBluetoothState = bluetoothState;
+ mBluetoothStateTimer[bluetoothState].startRunningLocked(elapsedRealtime);
}
}
int mWifiFullLockNesting = 0;
public void noteFullWifiLockAcquiredLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiFullLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWifiFullLockNesting++;
- getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked();
+ getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
}
public void noteFullWifiLockReleasedLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mWifiFullLockNesting--;
if (mWifiFullLockNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
- getUidStatsLocked(uid).noteFullWifiLockReleasedLocked();
+ getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
}
int mWifiScanNesting = 0;
public void noteWifiScanStartedLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiScanNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan started for: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWifiScanNesting++;
- getUidStatsLocked(uid).noteWifiScanStartedLocked();
+ getUidStatsLocked(uid).noteWifiScanStartedLocked(elapsedRealtime);
}
public void noteWifiScanStoppedLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mWifiScanNesting--;
if (mWifiScanNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan stopped for: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
- getUidStatsLocked(uid).noteWifiScanStoppedLocked();
+ getUidStatsLocked(uid).noteWifiScanStoppedLocked(elapsedRealtime);
}
public void noteWifiBatchedScanStartedLocked(int uid, int csph) {
- getUidStatsLocked(uid).noteWifiBatchedScanStartedLocked(csph);
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ getUidStatsLocked(uid).noteWifiBatchedScanStartedLocked(csph, elapsedRealtime);
}
public void noteWifiBatchedScanStoppedLocked(int uid) {
- getUidStatsLocked(uid).noteWifiBatchedScanStoppedLocked();
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ getUidStatsLocked(uid).noteWifiBatchedScanStoppedLocked(elapsedRealtime);
}
int mWifiMulticastNesting = 0;
public void noteWifiMulticastEnabledLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiMulticastNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWifiMulticastNesting++;
- getUidStatsLocked(uid).noteWifiMulticastEnabledLocked();
+ getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
}
public void noteWifiMulticastDisabledLocked(int uid) {
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mWifiMulticastNesting--;
if (mWifiMulticastNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
- getUidStatsLocked(uid).noteWifiMulticastDisabledLocked();
+ getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
}
public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
@@ -2512,16 +3423,45 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ private static String[] includeInStringArray(String[] array, String str) {
+ if (ArrayUtils.indexOf(array, str) >= 0) {
+ return array;
+ }
+ String[] newArray = new String[array.length+1];
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ newArray[array.length] = str;
+ return newArray;
+ }
+
+ private static String[] excludeFromStringArray(String[] array, String str) {
+ int index = ArrayUtils.indexOf(array, str);
+ if (index >= 0) {
+ String[] newArray = new String[array.length-1];
+ if (index > 0) {
+ System.arraycopy(array, 0, newArray, 0, index);
+ }
+ if (index < array.length-1) {
+ System.arraycopy(array, index+1, newArray, index, array.length-index-1);
+ }
+ return newArray;
+ }
+ return array;
+ }
+
public void noteNetworkInterfaceTypeLocked(String iface, int networkType) {
if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
- mMobileIfaces.add(iface);
+ mMobileIfaces = includeInStringArray(mMobileIfaces, iface);
+ if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mMobileIfaces);
} else {
- mMobileIfaces.remove(iface);
+ mMobileIfaces = excludeFromStringArray(mMobileIfaces, iface);
+ if (DEBUG) Slog.d(TAG, "Note non-mobile iface " + iface + ": " + mMobileIfaces);
}
if (ConnectivityManager.isNetworkTypeWifi(networkType)) {
- mWifiIfaces.add(iface);
+ mWifiIfaces = includeInStringArray(mWifiIfaces, iface);
+ if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces);
} else {
- mWifiIfaces.remove(iface);
+ mWifiIfaces = excludeFromStringArray(mWifiIfaces, iface);
+ if (DEBUG) Slog.d(TAG, "Note non-wifi iface " + iface + ": " + mWifiIfaces);
}
}
@@ -2529,37 +3469,53 @@ public final class BatteryStatsImpl extends BatteryStats {
// During device boot, qtaguid isn't enabled until after the inital
// loading of battery stats. Now that they're enabled, take our initial
// snapshot for future delta calculation.
- updateNetworkActivityLocked();
+ updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime());
+ }
+
+ @Override public long getScreenOnTime(long elapsedRealtimeUs, int which) {
+ return mScreenOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
- @Override public long getScreenOnTime(long batteryRealtime, int which) {
- return mScreenOnTimer.getTotalTimeLocked(batteryRealtime, which);
+ @Override public int getScreenOnCount(int which) {
+ return mScreenOnTimer.getCountLocked(which);
}
@Override public long getScreenBrightnessTime(int brightnessBin,
- long batteryRealtime, int which) {
+ long elapsedRealtimeUs, int which) {
return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked(
- batteryRealtime, which);
+ elapsedRealtimeUs, which);
}
- @Override public long getInteractiveTime(long batteryRealtime, int which) {
- return mInteractiveTimer.getTotalTimeLocked(batteryRealtime, which);
+ @Override public long getInteractiveTime(long elapsedRealtimeUs, int which) {
+ return mInteractiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
- @Override public long getPhoneOnTime(long batteryRealtime, int which) {
- return mPhoneOnTimer.getTotalTimeLocked(batteryRealtime, which);
+ @Override public long getLowPowerModeEnabledTime(long elapsedRealtimeUs, int which) {
+ return mLowPowerModeEnabledTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public int getLowPowerModeEnabledCount(int which) {
+ return mLowPowerModeEnabledTimer.getCountLocked(which);
+ }
+
+ @Override public long getPhoneOnTime(long elapsedRealtimeUs, int which) {
+ return mPhoneOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public int getPhoneOnCount(int which) {
+ return mPhoneOnTimer.getCountLocked(which);
}
@Override public long getPhoneSignalStrengthTime(int strengthBin,
- long batteryRealtime, int which) {
+ long elapsedRealtimeUs, int which) {
return mPhoneSignalStrengthsTimer[strengthBin].getTotalTimeLocked(
- batteryRealtime, which);
+ elapsedRealtimeUs, which);
}
@Override public long getPhoneSignalScanningTime(
- long batteryRealtime, int which) {
+ long elapsedRealtimeUs, int which) {
return mPhoneSignalScanningTimer.getTotalTimeLocked(
- batteryRealtime, which);
+ elapsedRealtimeUs, which);
}
@Override public int getPhoneSignalStrengthCount(int strengthBin, int which) {
@@ -2567,36 +3523,89 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override public long getPhoneDataConnectionTime(int dataType,
- long batteryRealtime, int which) {
+ long elapsedRealtimeUs, int which) {
return mPhoneDataConnectionsTimer[dataType].getTotalTimeLocked(
- batteryRealtime, which);
+ elapsedRealtimeUs, which);
}
@Override public int getPhoneDataConnectionCount(int dataType, int which) {
return mPhoneDataConnectionsTimer[dataType].getCountLocked(which);
}
- @Override public long getWifiOnTime(long batteryRealtime, int which) {
- return mWifiOnTimer.getTotalTimeLocked(batteryRealtime, which);
+ @Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) {
+ return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public int getMobileRadioActiveCount(int which) {
+ return mMobileRadioActiveTimer.getCountLocked(which);
+ }
+
+ @Override public long getMobileRadioActiveAdjustedTime(int which) {
+ return mMobileRadioActiveAdjustedTime.getCountLocked(which);
}
- @Override public long getGlobalWifiRunningTime(long batteryRealtime, int which) {
- return mGlobalWifiRunningTimer.getTotalTimeLocked(batteryRealtime, which);
+ @Override public long getMobileRadioActiveUnknownTime(int which) {
+ return mMobileRadioActiveUnknownTime.getCountLocked(which);
}
- @Override public long getBluetoothOnTime(long batteryRealtime, int which) {
- return mBluetoothOnTimer.getTotalTimeLocked(batteryRealtime, which);
+ @Override public int getMobileRadioActiveUnknownCount(int which) {
+ return (int)mMobileRadioActiveUnknownCount.getCountLocked(which);
+ }
+
+ @Override public long getWifiOnTime(long elapsedRealtimeUs, int which) {
+ return mWifiOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public long getGlobalWifiRunningTime(long elapsedRealtimeUs, int which) {
+ return mGlobalWifiRunningTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public long getWifiStateTime(int wifiState,
+ long elapsedRealtimeUs, int which) {
+ return mWifiStateTimer[wifiState].getTotalTimeLocked(
+ elapsedRealtimeUs, which);
+ }
+
+ @Override public int getWifiStateCount(int wifiState, int which) {
+ return mWifiStateTimer[wifiState].getCountLocked(which);
+ }
+
+ @Override public long getBluetoothOnTime(long elapsedRealtimeUs, int which) {
+ return mBluetoothOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public long getBluetoothStateTime(int bluetoothState,
+ long elapsedRealtimeUs, int which) {
+ return mBluetoothStateTimer[bluetoothState].getTotalTimeLocked(
+ elapsedRealtimeUs, which);
+ }
+
+ @Override public int getBluetoothStateCount(int bluetoothState, int which) {
+ return mBluetoothStateTimer[bluetoothState].getCountLocked(which);
+ }
+
+ @Override
+ public long getNetworkActivityBytes(int type, int which) {
+ if (type >= 0 && type < mNetworkByteActivityCounters.length) {
+ return mNetworkByteActivityCounters[type].getCountLocked(which);
+ } else {
+ return 0;
+ }
}
@Override
- public long getNetworkActivityCount(int type, int which) {
- if (type >= 0 && type < mNetworkActivityCounters.length) {
- return mNetworkActivityCounters[type].getCountLocked(which);
+ public long getNetworkActivityPackets(int type, int which) {
+ if (type >= 0 && type < mNetworkPacketActivityCounters.length) {
+ return mNetworkPacketActivityCounters[type].getCountLocked(which);
} else {
return 0;
}
}
+ @Override public long getStartClockTime() {
+ return mStartClockTime;
+ }
+
@Override public boolean getIsOnBattery() {
return mOnBattery;
}
@@ -2640,7 +3649,10 @@ public final class BatteryStatsImpl extends BatteryStats {
Counter[] mUserActivityCounters;
- LongSamplingCounter[] mNetworkActivityCounters;
+ LongSamplingCounter[] mNetworkByteActivityCounters;
+ LongSamplingCounter[] mNetworkPacketActivityCounters;
+ LongSamplingCounter mMobileRadioActiveTime;
+ LongSamplingCounter mMobileRadioActiveCount;
/**
* The statistics we have collected for this uid's wake locks.
@@ -2670,14 +3682,14 @@ public final class BatteryStatsImpl extends BatteryStats {
public Uid(int uid) {
mUid = uid;
mWifiRunningTimer = new StopwatchTimer(Uid.this, WIFI_RUNNING,
- mWifiRunningTimers, mUnpluggables);
+ mWifiRunningTimers, mOnBatteryTimeBase);
mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK,
- mFullWifiLockTimers, mUnpluggables);
+ mFullWifiLockTimers, mOnBatteryTimeBase);
mWifiScanTimer = new StopwatchTimer(Uid.this, WIFI_SCAN,
- mWifiScanTimers, mUnpluggables);
+ mWifiScanTimers, mOnBatteryTimeBase);
mWifiBatchedScanTimer = new StopwatchTimer[NUM_WIFI_BATCHED_SCAN_BINS];
mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
- mWifiMulticastTimers, mUnpluggables);
+ mWifiMulticastTimers, mOnBatteryTimeBase);
}
@Override
@@ -2706,67 +3718,67 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
- public void noteWifiRunningLocked() {
+ public void noteWifiRunningLocked(long elapsedRealtimeMs) {
if (!mWifiRunning) {
mWifiRunning = true;
if (mWifiRunningTimer == null) {
mWifiRunningTimer = new StopwatchTimer(Uid.this, WIFI_RUNNING,
- mWifiRunningTimers, mUnpluggables);
+ mWifiRunningTimers, mOnBatteryTimeBase);
}
- mWifiRunningTimer.startRunningLocked(BatteryStatsImpl.this);
+ mWifiRunningTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@Override
- public void noteWifiStoppedLocked() {
+ public void noteWifiStoppedLocked(long elapsedRealtimeMs) {
if (mWifiRunning) {
mWifiRunning = false;
- mWifiRunningTimer.stopRunningLocked(BatteryStatsImpl.this);
+ mWifiRunningTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@Override
- public void noteFullWifiLockAcquiredLocked() {
+ public void noteFullWifiLockAcquiredLocked(long elapsedRealtimeMs) {
if (!mFullWifiLockOut) {
mFullWifiLockOut = true;
if (mFullWifiLockTimer == null) {
mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK,
- mFullWifiLockTimers, mUnpluggables);
+ mFullWifiLockTimers, mOnBatteryTimeBase);
}
- mFullWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
+ mFullWifiLockTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@Override
- public void noteFullWifiLockReleasedLocked() {
+ public void noteFullWifiLockReleasedLocked(long elapsedRealtimeMs) {
if (mFullWifiLockOut) {
mFullWifiLockOut = false;
- mFullWifiLockTimer.stopRunningLocked(BatteryStatsImpl.this);
+ mFullWifiLockTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@Override
- public void noteWifiScanStartedLocked() {
+ public void noteWifiScanStartedLocked(long elapsedRealtimeMs) {
if (!mWifiScanStarted) {
mWifiScanStarted = true;
if (mWifiScanTimer == null) {
mWifiScanTimer = new StopwatchTimer(Uid.this, WIFI_SCAN,
- mWifiScanTimers, mUnpluggables);
+ mWifiScanTimers, mOnBatteryTimeBase);
}
- mWifiScanTimer.startRunningLocked(BatteryStatsImpl.this);
+ mWifiScanTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@Override
- public void noteWifiScanStoppedLocked() {
+ public void noteWifiScanStoppedLocked(long elapsedRealtimeMs) {
if (mWifiScanStarted) {
mWifiScanStarted = false;
- mWifiScanTimer.stopRunningLocked(BatteryStatsImpl.this);
+ mWifiScanTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@Override
- public void noteWifiBatchedScanStartedLocked(int csph) {
+ public void noteWifiBatchedScanStartedLocked(int csph, long elapsedRealtimeMs) {
int bin = 0;
while (csph > 8 && bin < NUM_WIFI_BATCHED_SCAN_BINS) {
csph = csph >> 3;
@@ -2777,66 +3789,66 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED) {
mWifiBatchedScanTimer[mWifiBatchedScanBinStarted].
- stopRunningLocked(BatteryStatsImpl.this);
+ stopRunningLocked(elapsedRealtimeMs);
}
mWifiBatchedScanBinStarted = bin;
if (mWifiBatchedScanTimer[bin] == null) {
makeWifiBatchedScanBin(bin, null);
}
- mWifiBatchedScanTimer[bin].startRunningLocked(BatteryStatsImpl.this);
+ mWifiBatchedScanTimer[bin].startRunningLocked(elapsedRealtimeMs);
}
@Override
- public void noteWifiBatchedScanStoppedLocked() {
+ public void noteWifiBatchedScanStoppedLocked(long elapsedRealtimeMs) {
if (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED) {
mWifiBatchedScanTimer[mWifiBatchedScanBinStarted].
- stopRunningLocked(BatteryStatsImpl.this);
+ stopRunningLocked(elapsedRealtimeMs);
mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED;
}
}
@Override
- public void noteWifiMulticastEnabledLocked() {
+ public void noteWifiMulticastEnabledLocked(long elapsedRealtimeMs) {
if (!mWifiMulticastEnabled) {
mWifiMulticastEnabled = true;
if (mWifiMulticastTimer == null) {
mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
- mWifiMulticastTimers, mUnpluggables);
+ mWifiMulticastTimers, mOnBatteryTimeBase);
}
- mWifiMulticastTimer.startRunningLocked(BatteryStatsImpl.this);
+ mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@Override
- public void noteWifiMulticastDisabledLocked() {
+ public void noteWifiMulticastDisabledLocked(long elapsedRealtimeMs) {
if (mWifiMulticastEnabled) {
mWifiMulticastEnabled = false;
- mWifiMulticastTimer.stopRunningLocked(BatteryStatsImpl.this);
+ mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
public StopwatchTimer createAudioTurnedOnTimerLocked() {
if (mAudioTurnedOnTimer == null) {
mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
- null, mUnpluggables);
+ null, mOnBatteryTimeBase);
}
return mAudioTurnedOnTimer;
}
@Override
- public void noteAudioTurnedOnLocked() {
+ public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
if (!mAudioTurnedOn) {
mAudioTurnedOn = true;
- createAudioTurnedOnTimerLocked().startRunningLocked(BatteryStatsImpl.this);
+ createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
}
@Override
- public void noteAudioTurnedOffLocked() {
+ public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOn) {
mAudioTurnedOn = false;
if (mAudioTurnedOnTimer != null) {
- mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+ mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
}
@@ -2844,25 +3856,25 @@ public final class BatteryStatsImpl extends BatteryStats {
public StopwatchTimer createVideoTurnedOnTimerLocked() {
if (mVideoTurnedOnTimer == null) {
mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
- null, mUnpluggables);
+ null, mOnBatteryTimeBase);
}
return mVideoTurnedOnTimer;
}
@Override
- public void noteVideoTurnedOnLocked() {
+ public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
if (!mVideoTurnedOn) {
mVideoTurnedOn = true;
- createVideoTurnedOnTimerLocked().startRunningLocked(BatteryStatsImpl.this);
+ createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
}
@Override
- public void noteVideoTurnedOffLocked() {
+ public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOn) {
mVideoTurnedOn = false;
if (mVideoTurnedOnTimer != null) {
- mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+ mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
}
@@ -2870,28 +3882,27 @@ public final class BatteryStatsImpl extends BatteryStats {
public StopwatchTimer createForegroundActivityTimerLocked() {
if (mForegroundActivityTimer == null) {
mForegroundActivityTimer = new StopwatchTimer(
- Uid.this, FOREGROUND_ACTIVITY, null, mUnpluggables);
+ Uid.this, FOREGROUND_ACTIVITY, null, mOnBatteryTimeBase);
}
return mForegroundActivityTimer;
}
@Override
- public void noteActivityResumedLocked() {
+ public void noteActivityResumedLocked(long elapsedRealtimeMs) {
// We always start, since we want multiple foreground PIDs to nest
- createForegroundActivityTimerLocked().startRunningLocked(BatteryStatsImpl.this);
+ createForegroundActivityTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
@Override
- public void noteActivityPausedLocked() {
+ public void noteActivityPausedLocked(long elapsedRealtimeMs) {
if (mForegroundActivityTimer != null) {
- mForegroundActivityTimer.stopRunningLocked(BatteryStatsImpl.this);
+ mForegroundActivityTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
public BatchTimer createVibratorOnTimerLocked() {
if (mVibratorOnTimer == null) {
- mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
- mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal);
+ mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON, mOnBatteryTimeBase);
}
return mVibratorOnTimer;
}
@@ -2907,61 +3918,60 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
- public long getWifiRunningTime(long batteryRealtime, int which) {
+ public long getWifiRunningTime(long elapsedRealtimeUs, int which) {
if (mWifiRunningTimer == null) {
return 0;
}
- return mWifiRunningTimer.getTotalTimeLocked(batteryRealtime, which);
+ return mWifiRunningTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
- public long getFullWifiLockTime(long batteryRealtime, int which) {
+ public long getFullWifiLockTime(long elapsedRealtimeUs, int which) {
if (mFullWifiLockTimer == null) {
return 0;
}
- return mFullWifiLockTimer.getTotalTimeLocked(batteryRealtime, which);
+ return mFullWifiLockTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
- public long getWifiScanTime(long batteryRealtime, int which) {
+ public long getWifiScanTime(long elapsedRealtimeUs, int which) {
if (mWifiScanTimer == null) {
return 0;
}
- return mWifiScanTimer.getTotalTimeLocked(batteryRealtime, which);
+ return mWifiScanTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
- public long getWifiBatchedScanTime(int csphBin, long batteryRealtime, int which) {
+ public long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which) {
if (csphBin < 0 || csphBin >= NUM_WIFI_BATCHED_SCAN_BINS) return 0;
if (mWifiBatchedScanTimer[csphBin] == null) {
return 0;
}
- return mWifiBatchedScanTimer[csphBin].getTotalTimeLocked(batteryRealtime, which);
+ return mWifiBatchedScanTimer[csphBin].getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
- public long getWifiMulticastTime(long batteryRealtime, int which) {
+ public long getWifiMulticastTime(long elapsedRealtimeUs, int which) {
if (mWifiMulticastTimer == null) {
return 0;
}
- return mWifiMulticastTimer.getTotalTimeLocked(batteryRealtime,
- which);
+ return mWifiMulticastTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
- public long getAudioTurnedOnTime(long batteryRealtime, int which) {
+ public long getAudioTurnedOnTime(long elapsedRealtimeUs, int which) {
if (mAudioTurnedOnTimer == null) {
return 0;
}
- return mAudioTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
+ return mAudioTurnedOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
- public long getVideoTurnedOnTime(long batteryRealtime, int which) {
+ public long getVideoTurnedOnTime(long elapsedRealtimeUs, int which) {
if (mVideoTurnedOnTimer == null) {
return 0;
}
- return mVideoTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
+ return mVideoTurnedOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
@@ -3010,10 +4020,10 @@ public final class BatteryStatsImpl extends BatteryStats {
}
if (in == null) {
mWifiBatchedScanTimer[i] = new StopwatchTimer(this, WIFI_BATCHED_SCAN, collected,
- mUnpluggables);
+ mOnBatteryTimeBase);
} else {
mWifiBatchedScanTimer[i] = new StopwatchTimer(this, WIFI_BATCHED_SCAN, collected,
- mUnpluggables, in);
+ mOnBatteryTimeBase, in);
}
}
@@ -3021,42 +4031,77 @@ public final class BatteryStatsImpl extends BatteryStats {
void initUserActivityLocked() {
mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
- mUserActivityCounters[i] = new Counter(mUnpluggables);
+ mUserActivityCounters[i] = new Counter(mOnBatteryTimeBase);
}
}
- void noteNetworkActivityLocked(int type, long delta) {
- if (mNetworkActivityCounters == null) {
+ void noteNetworkActivityLocked(int type, long deltaBytes, long deltaPackets) {
+ if (mNetworkByteActivityCounters == null) {
initNetworkActivityLocked();
}
if (type >= 0 && type < NUM_NETWORK_ACTIVITY_TYPES) {
- mNetworkActivityCounters[type].addCountLocked(delta);
+ mNetworkByteActivityCounters[type].addCountLocked(deltaBytes);
+ mNetworkPacketActivityCounters[type].addCountLocked(deltaPackets);
} else {
Slog.w(TAG, "Unknown network activity type " + type + " was specified.",
new Throwable());
}
}
+ void noteMobileRadioActiveTimeLocked(long batteryUptime) {
+ if (mNetworkByteActivityCounters == null) {
+ initNetworkActivityLocked();
+ }
+ mMobileRadioActiveTime.addCountLocked(batteryUptime);
+ mMobileRadioActiveCount.addCountLocked(1);
+ }
+
@Override
public boolean hasNetworkActivity() {
- return mNetworkActivityCounters != null;
+ return mNetworkByteActivityCounters != null;
}
@Override
- public long getNetworkActivityCount(int type, int which) {
- if (mNetworkActivityCounters != null && type >= 0
- && type < mNetworkActivityCounters.length) {
- return mNetworkActivityCounters[type].getCountLocked(which);
+ public long getNetworkActivityBytes(int type, int which) {
+ if (mNetworkByteActivityCounters != null && type >= 0
+ && type < mNetworkByteActivityCounters.length) {
+ return mNetworkByteActivityCounters[type].getCountLocked(which);
} else {
return 0;
}
}
+ @Override
+ public long getNetworkActivityPackets(int type, int which) {
+ if (mNetworkPacketActivityCounters != null && type >= 0
+ && type < mNetworkPacketActivityCounters.length) {
+ return mNetworkPacketActivityCounters[type].getCountLocked(which);
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public long getMobileRadioActiveTime(int which) {
+ return mMobileRadioActiveTime != null
+ ? mMobileRadioActiveTime.getCountLocked(which) : 0;
+ }
+
+ @Override
+ public int getMobileRadioActiveCount(int which) {
+ return mMobileRadioActiveCount != null
+ ? (int)mMobileRadioActiveCount.getCountLocked(which) : 0;
+ }
+
void initNetworkActivityLocked() {
- mNetworkActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+ mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+ mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables);
+ mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
+ mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
}
+ mMobileRadioActiveTime = new LongSamplingCounter(mOnBatteryTimeBase);
+ mMobileRadioActiveCount = new LongSamplingCounter(mOnBatteryTimeBase);
}
/**
@@ -3067,42 +4112,42 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean active = false;
if (mWifiRunningTimer != null) {
- active |= !mWifiRunningTimer.reset(BatteryStatsImpl.this, false);
+ active |= !mWifiRunningTimer.reset(false);
active |= mWifiRunning;
}
if (mFullWifiLockTimer != null) {
- active |= !mFullWifiLockTimer.reset(BatteryStatsImpl.this, false);
+ active |= !mFullWifiLockTimer.reset(false);
active |= mFullWifiLockOut;
}
if (mWifiScanTimer != null) {
- active |= !mWifiScanTimer.reset(BatteryStatsImpl.this, false);
+ active |= !mWifiScanTimer.reset(false);
active |= mWifiScanStarted;
}
if (mWifiBatchedScanTimer != null) {
for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
if (mWifiBatchedScanTimer[i] != null) {
- active |= !mWifiBatchedScanTimer[i].reset(BatteryStatsImpl.this, false);
+ active |= !mWifiBatchedScanTimer[i].reset(false);
}
}
active |= (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED);
}
if (mWifiMulticastTimer != null) {
- active |= !mWifiMulticastTimer.reset(BatteryStatsImpl.this, false);
+ active |= !mWifiMulticastTimer.reset(false);
active |= mWifiMulticastEnabled;
}
if (mAudioTurnedOnTimer != null) {
- active |= !mAudioTurnedOnTimer.reset(BatteryStatsImpl.this, false);
+ active |= !mAudioTurnedOnTimer.reset(false);
active |= mAudioTurnedOn;
}
if (mVideoTurnedOnTimer != null) {
- active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
+ active |= !mVideoTurnedOnTimer.reset(false);
active |= mVideoTurnedOn;
}
if (mForegroundActivityTimer != null) {
- active |= !mForegroundActivityTimer.reset(BatteryStatsImpl.this, false);
+ active |= !mForegroundActivityTimer.reset(false);
}
if (mVibratorOnTimer != null) {
- if (mVibratorOnTimer.reset(BatteryStatsImpl.this, false)) {
+ if (mVibratorOnTimer.reset(false)) {
mVibratorOnTimer.detach();
mVibratorOnTimer = null;
} else {
@@ -3116,10 +4161,13 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- if (mNetworkActivityCounters != null) {
+ if (mNetworkByteActivityCounters != null) {
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i].reset(false);
+ mNetworkByteActivityCounters[i].reset(false);
+ mNetworkPacketActivityCounters[i].reset(false);
}
+ mMobileRadioActiveTime.reset(false);
+ mMobileRadioActiveCount.reset(false);
}
if (mWakelockStats.size() > 0) {
@@ -3155,10 +4203,12 @@ public final class BatteryStatsImpl extends BatteryStats {
mProcessStats.clear();
}
if (mPids.size() > 0) {
- for (int i=0; !active && i<mPids.size(); i++) {
+ for (int i=mPids.size()-1; i>=0; i--) {
Pid pid = mPids.valueAt(i);
- if (pid.mWakeStart != 0) {
+ if (pid.mWakeNesting > 0) {
active = true;
+ } else {
+ mPids.removeAt(i);
}
}
}
@@ -3180,8 +4230,6 @@ public final class BatteryStatsImpl extends BatteryStats {
mPackageStats.clear();
}
- mPids.clear();
-
if (!active) {
if (mWifiRunningTimer != null) {
mWifiRunningTimer.detach();
@@ -3217,29 +4265,31 @@ public final class BatteryStatsImpl extends BatteryStats {
mUserActivityCounters[i].detach();
}
}
- if (mNetworkActivityCounters != null) {
+ if (mNetworkByteActivityCounters != null) {
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i].detach();
+ mNetworkByteActivityCounters[i].detach();
+ mNetworkPacketActivityCounters[i].detach();
}
}
+ mPids.clear();
}
return !active;
}
- void writeToParcelLocked(Parcel out, long batteryRealtime) {
+ void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) {
out.writeInt(mWakelockStats.size());
for (Map.Entry<String, Uid.Wakelock> wakelockEntry : mWakelockStats.entrySet()) {
out.writeString(wakelockEntry.getKey());
Uid.Wakelock wakelock = wakelockEntry.getValue();
- wakelock.writeToParcelLocked(out, batteryRealtime);
+ wakelock.writeToParcelLocked(out, elapsedRealtimeUs);
}
out.writeInt(mSensorStats.size());
for (Map.Entry<Integer, Uid.Sensor> sensorEntry : mSensorStats.entrySet()) {
out.writeInt(sensorEntry.getKey());
Uid.Sensor sensor = sensorEntry.getValue();
- sensor.writeToParcelLocked(out, batteryRealtime);
+ sensor.writeToParcelLocked(out, elapsedRealtimeUs);
}
out.writeInt(mProcessStats.size());
@@ -3258,57 +4308,57 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mWifiRunningTimer != null) {
out.writeInt(1);
- mWifiRunningTimer.writeToParcel(out, batteryRealtime);
+ mWifiRunningTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mFullWifiLockTimer != null) {
out.writeInt(1);
- mFullWifiLockTimer.writeToParcel(out, batteryRealtime);
+ mFullWifiLockTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mWifiScanTimer != null) {
out.writeInt(1);
- mWifiScanTimer.writeToParcel(out, batteryRealtime);
+ mWifiScanTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
if (mWifiBatchedScanTimer[i] != null) {
out.writeInt(1);
- mWifiBatchedScanTimer[i].writeToParcel(out, batteryRealtime);
+ mWifiBatchedScanTimer[i].writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
}
if (mWifiMulticastTimer != null) {
out.writeInt(1);
- mWifiMulticastTimer.writeToParcel(out, batteryRealtime);
+ mWifiMulticastTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mAudioTurnedOnTimer != null) {
out.writeInt(1);
- mAudioTurnedOnTimer.writeToParcel(out, batteryRealtime);
+ mAudioTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mVideoTurnedOnTimer != null) {
out.writeInt(1);
- mVideoTurnedOnTimer.writeToParcel(out, batteryRealtime);
+ mVideoTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mForegroundActivityTimer != null) {
out.writeInt(1);
- mForegroundActivityTimer.writeToParcel(out, batteryRealtime);
+ mForegroundActivityTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mVibratorOnTimer != null) {
out.writeInt(1);
- mVibratorOnTimer.writeToParcel(out, batteryRealtime);
+ mVibratorOnTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
@@ -3320,23 +4370,26 @@ public final class BatteryStatsImpl extends BatteryStats {
} else {
out.writeInt(0);
}
- if (mNetworkActivityCounters != null) {
+ if (mNetworkByteActivityCounters != null) {
out.writeInt(1);
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i].writeToParcel(out);
+ mNetworkByteActivityCounters[i].writeToParcel(out);
+ mNetworkPacketActivityCounters[i].writeToParcel(out);
}
+ mMobileRadioActiveTime.writeToParcel(out);
+ mMobileRadioActiveCount.writeToParcel(out);
} else {
out.writeInt(0);
}
}
- void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
+ void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
int numWakelocks = in.readInt();
mWakelockStats.clear();
for (int j = 0; j < numWakelocks; j++) {
String wakelockName = in.readString();
Uid.Wakelock wakelock = new Wakelock();
- wakelock.readFromParcelLocked(unpluggables, in);
+ wakelock.readFromParcelLocked(timeBase, screenOffTimeBase, in);
// We will just drop some random set of wakelocks if
// the previous run of the system was an older version
// that didn't impose a limit.
@@ -3348,7 +4401,7 @@ public final class BatteryStatsImpl extends BatteryStats {
for (int k = 0; k < numSensors; k++) {
int sensorNumber = in.readInt();
Uid.Sensor sensor = new Sensor(sensorNumber);
- sensor.readFromParcelLocked(mUnpluggables, in);
+ sensor.readFromParcelLocked(mOnBatteryTimeBase, in);
mSensorStats.put(sensorNumber, sensor);
}
@@ -3373,21 +4426,21 @@ public final class BatteryStatsImpl extends BatteryStats {
mWifiRunning = false;
if (in.readInt() != 0) {
mWifiRunningTimer = new StopwatchTimer(Uid.this, WIFI_RUNNING,
- mWifiRunningTimers, mUnpluggables, in);
+ mWifiRunningTimers, mOnBatteryTimeBase, in);
} else {
mWifiRunningTimer = null;
}
mFullWifiLockOut = false;
if (in.readInt() != 0) {
mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK,
- mFullWifiLockTimers, mUnpluggables, in);
+ mFullWifiLockTimers, mOnBatteryTimeBase, in);
} else {
mFullWifiLockTimer = null;
}
mWifiScanStarted = false;
if (in.readInt() != 0) {
mWifiScanTimer = new StopwatchTimer(Uid.this, WIFI_SCAN,
- mWifiScanTimers, mUnpluggables, in);
+ mWifiScanTimers, mOnBatteryTimeBase, in);
} else {
mWifiScanTimer = null;
}
@@ -3402,51 +4455,58 @@ public final class BatteryStatsImpl extends BatteryStats {
mWifiMulticastEnabled = false;
if (in.readInt() != 0) {
mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
- mWifiMulticastTimers, mUnpluggables, in);
+ mWifiMulticastTimers, mOnBatteryTimeBase, in);
} else {
mWifiMulticastTimer = null;
}
mAudioTurnedOn = false;
if (in.readInt() != 0) {
mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
- null, mUnpluggables, in);
+ null, mOnBatteryTimeBase, in);
} else {
mAudioTurnedOnTimer = null;
}
mVideoTurnedOn = false;
if (in.readInt() != 0) {
mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
- null, mUnpluggables, in);
+ null, mOnBatteryTimeBase, in);
} else {
mVideoTurnedOnTimer = null;
}
if (in.readInt() != 0) {
mForegroundActivityTimer = new StopwatchTimer(
- Uid.this, FOREGROUND_ACTIVITY, null, mUnpluggables, in);
+ Uid.this, FOREGROUND_ACTIVITY, null, mOnBatteryTimeBase, in);
} else {
mForegroundActivityTimer = null;
}
if (in.readInt() != 0) {
- mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
- mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal, in);
+ mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON, mOnBatteryTimeBase, in);
} else {
mVibratorOnTimer = null;
}
if (in.readInt() != 0) {
mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
- mUserActivityCounters[i] = new Counter(mUnpluggables, in);
+ mUserActivityCounters[i] = new Counter(mOnBatteryTimeBase, in);
}
} else {
mUserActivityCounters = null;
}
if (in.readInt() != 0) {
- mNetworkActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+ mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+ mNetworkPacketActivityCounters
+ = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables, in);
+ mNetworkByteActivityCounters[i]
+ = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ mNetworkPacketActivityCounters[i]
+ = new LongSamplingCounter(mOnBatteryTimeBase, in);
}
+ mMobileRadioActiveTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ mMobileRadioActiveCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
} else {
- mNetworkActivityCounters = null;
+ mNetworkByteActivityCounters = null;
+ mNetworkPacketActivityCounters = null;
}
}
@@ -3477,24 +4537,24 @@ public final class BatteryStatsImpl extends BatteryStats {
* return a new Timer, or null.
*/
private StopwatchTimer readTimerFromParcel(int type, ArrayList<StopwatchTimer> pool,
- ArrayList<Unpluggable> unpluggables, Parcel in) {
+ TimeBase timeBase, Parcel in) {
if (in.readInt() == 0) {
return null;
}
- return new StopwatchTimer(Uid.this, type, pool, unpluggables, in);
+ return new StopwatchTimer(Uid.this, type, pool, timeBase, in);
}
boolean reset() {
boolean wlactive = false;
if (mTimerFull != null) {
- wlactive |= !mTimerFull.reset(BatteryStatsImpl.this, false);
+ wlactive |= !mTimerFull.reset(false);
}
if (mTimerPartial != null) {
- wlactive |= !mTimerPartial.reset(BatteryStatsImpl.this, false);
+ wlactive |= !mTimerPartial.reset(false);
}
if (mTimerWindow != null) {
- wlactive |= !mTimerWindow.reset(BatteryStatsImpl.this, false);
+ wlactive |= !mTimerWindow.reset(false);
}
if (!wlactive) {
if (mTimerFull != null) {
@@ -3513,19 +4573,19 @@ public final class BatteryStatsImpl extends BatteryStats {
return !wlactive;
}
- void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
+ void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
mTimerPartial = readTimerFromParcel(WAKE_TYPE_PARTIAL,
- mPartialTimers, unpluggables, in);
+ mPartialTimers, screenOffTimeBase, in);
mTimerFull = readTimerFromParcel(WAKE_TYPE_FULL,
- mFullTimers, unpluggables, in);
+ mFullTimers, timeBase, in);
mTimerWindow = readTimerFromParcel(WAKE_TYPE_WINDOW,
- mWindowTimers, unpluggables, in);
+ mWindowTimers, timeBase, in);
}
- void writeToParcelLocked(Parcel out, long batteryRealtime) {
- Timer.writeTimerToParcel(out, mTimerPartial, batteryRealtime);
- Timer.writeTimerToParcel(out, mTimerFull, batteryRealtime);
- Timer.writeTimerToParcel(out, mTimerWindow, batteryRealtime);
+ void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) {
+ Timer.writeTimerToParcel(out, mTimerPartial, elapsedRealtimeUs);
+ Timer.writeTimerToParcel(out, mTimerFull, elapsedRealtimeUs);
+ Timer.writeTimerToParcel(out, mTimerWindow, elapsedRealtimeUs);
}
@Override
@@ -3547,8 +4607,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mHandle = handle;
}
- private StopwatchTimer readTimerFromParcel(ArrayList<Unpluggable> unpluggables,
- Parcel in) {
+ private StopwatchTimer readTimerFromParcel(TimeBase timeBase, Parcel in) {
if (in.readInt() == 0) {
return null;
}
@@ -3558,23 +4617,23 @@ public final class BatteryStatsImpl extends BatteryStats {
pool = new ArrayList<StopwatchTimer>();
mSensorTimers.put(mHandle, pool);
}
- return new StopwatchTimer(Uid.this, 0, pool, unpluggables, in);
+ return new StopwatchTimer(Uid.this, 0, pool, timeBase, in);
}
boolean reset() {
- if (mTimer.reset(BatteryStatsImpl.this, true)) {
+ if (mTimer.reset(true)) {
mTimer = null;
return true;
}
return false;
}
- void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
- mTimer = readTimerFromParcel(unpluggables, in);
+ void readFromParcelLocked(TimeBase timeBase, Parcel in) {
+ mTimer = readTimerFromParcel(timeBase, in);
}
- void writeToParcelLocked(Parcel out, long batteryRealtime) {
- Timer.writeTimerToParcel(out, mTimer, batteryRealtime);
+ void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) {
+ Timer.writeTimerToParcel(out, mTimer, elapsedRealtimeUs);
}
@Override
@@ -3591,7 +4650,12 @@ public final class BatteryStatsImpl extends BatteryStats {
/**
* The statistics associated with a particular process.
*/
- public final class Proc extends BatteryStats.Uid.Proc implements Unpluggable {
+ public final class Proc extends BatteryStats.Uid.Proc implements TimeBaseObs {
+ /**
+ * Remains true until removed from the stats.
+ */
+ boolean mActive = true;
+
/**
* Total time (in 1/100 sec) spent executing in user code.
*/
@@ -3677,26 +4741,27 @@ public final class BatteryStatsImpl extends BatteryStats {
ArrayList<ExcessivePower> mExcessivePower;
Proc() {
- mUnpluggables.add(this);
+ mOnBatteryTimeBase.add(this);
mSpeedBins = new SamplingCounter[getCpuSpeedSteps()];
}
- public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
mUnpluggedUserTime = mUserTime;
mUnpluggedSystemTime = mSystemTime;
mUnpluggedForegroundTime = mForegroundTime;
mUnpluggedStarts = mStarts;
}
- public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
}
void detach() {
- mUnpluggables.remove(this);
+ mActive = false;
+ mOnBatteryTimeBase.remove(this);
for (int i = 0; i < mSpeedBins.length; i++) {
SamplingCounter c = mSpeedBins[i];
if (c != null) {
- mUnpluggables.remove(c);
+ mOnBatteryTimeBase.remove(c);
mSpeedBins[i] = null;
}
}
@@ -3825,7 +4890,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mSpeedBins = new SamplingCounter[bins >= steps ? bins : steps];
for (int i = 0; i < bins; i++) {
if (in.readInt() != 0) {
- mSpeedBins[i] = new SamplingCounter(mUnpluggables, in);
+ mSpeedBins[i] = new SamplingCounter(mOnBatteryTimeBase, in);
}
}
@@ -3850,65 +4915,50 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
+ public boolean isActive() {
+ return mActive;
+ }
+
+ @Override
public long getUserTime(int which) {
- long val;
- if (which == STATS_LAST) {
- val = mLastUserTime;
- } else {
- val = mUserTime;
- if (which == STATS_CURRENT) {
- val -= mLoadedUserTime;
- } else if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedUserTime;
- }
+ long val = mUserTime;
+ if (which == STATS_CURRENT) {
+ val -= mLoadedUserTime;
+ } else if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedUserTime;
}
return val;
}
@Override
public long getSystemTime(int which) {
- long val;
- if (which == STATS_LAST) {
- val = mLastSystemTime;
- } else {
- val = mSystemTime;
- if (which == STATS_CURRENT) {
- val -= mLoadedSystemTime;
- } else if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedSystemTime;
- }
+ long val = mSystemTime;
+ if (which == STATS_CURRENT) {
+ val -= mLoadedSystemTime;
+ } else if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedSystemTime;
}
return val;
}
@Override
public long getForegroundTime(int which) {
- long val;
- if (which == STATS_LAST) {
- val = mLastForegroundTime;
- } else {
- val = mForegroundTime;
- if (which == STATS_CURRENT) {
- val -= mLoadedForegroundTime;
- } else if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedForegroundTime;
- }
+ long val = mForegroundTime;
+ if (which == STATS_CURRENT) {
+ val -= mLoadedForegroundTime;
+ } else if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedForegroundTime;
}
return val;
}
@Override
public int getStarts(int which) {
- int val;
- if (which == STATS_LAST) {
- val = mLastStarts;
- } else {
- val = mStarts;
- if (which == STATS_CURRENT) {
- val -= mLoadedStarts;
- } else if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedStarts;
- }
+ int val = mStarts;
+ if (which == STATS_CURRENT) {
+ val -= mLoadedStarts;
+ } else if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedStarts;
}
return val;
}
@@ -3920,7 +4970,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (amt != 0) {
SamplingCounter c = mSpeedBins[i];
if (c == null) {
- mSpeedBins[i] = c = new SamplingCounter(mUnpluggables);
+ mSpeedBins[i] = c = new SamplingCounter(mOnBatteryTimeBase);
}
c.addCountAtomic(values[i]);
}
@@ -3941,7 +4991,7 @@ public final class BatteryStatsImpl extends BatteryStats {
/**
* The statistics associated with a particular package.
*/
- public final class Pkg extends BatteryStats.Uid.Pkg implements Unpluggable {
+ public final class Pkg extends BatteryStats.Uid.Pkg implements TimeBaseObs {
/**
* Number of times this package has done something that could wake up the
* device from sleep.
@@ -3972,18 +5022,18 @@ public final class BatteryStatsImpl extends BatteryStats {
final HashMap<String, Serv> mServiceStats = new HashMap<String, Serv>();
Pkg() {
- mUnpluggables.add(this);
+ mOnBatteryScreenOffTimeBase.add(this);
}
- public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
mUnpluggedWakeups = mWakeups;
}
- public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
}
void detach() {
- mUnpluggables.remove(this);
+ mOnBatteryScreenOffTimeBase.remove(this);
}
void readFromParcelLocked(Parcel in) {
@@ -4024,16 +5074,11 @@ public final class BatteryStatsImpl extends BatteryStats {
@Override
public int getWakeups(int which) {
- int val;
- if (which == STATS_LAST) {
- val = mLastWakeups;
- } else {
- val = mWakeups;
- if (which == STATS_CURRENT) {
- val -= mLoadedWakeups;
- } else if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedWakeups;
- }
+ int val = mWakeups;
+ if (which == STATS_CURRENT) {
+ val -= mLoadedWakeups;
+ } else if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedWakeups;
}
return val;
@@ -4042,7 +5087,7 @@ public final class BatteryStatsImpl extends BatteryStats {
/**
* The statistics associated with a particular service.
*/
- public final class Serv extends BatteryStats.Uid.Pkg.Serv implements Unpluggable {
+ public final class Serv extends BatteryStats.Uid.Pkg.Serv implements TimeBaseObs {
/**
* Total time (ms in battery uptime) the service has been left started.
*/
@@ -4134,20 +5179,22 @@ public final class BatteryStatsImpl extends BatteryStats {
int mUnpluggedLaunches;
Serv() {
- mUnpluggables.add(this);
+ mOnBatteryTimeBase.add(this);
}
- public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
- mUnpluggedStartTime = getStartTimeToNowLocked(batteryUptime);
+ public void onTimeStarted(long elapsedRealtime, long baseUptime,
+ long baseRealtime) {
+ mUnpluggedStartTime = getStartTimeToNowLocked(baseUptime);
mUnpluggedStarts = mStarts;
mUnpluggedLaunches = mLaunches;
}
- public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+ public void onTimeStopped(long elapsedRealtime, long baseUptime,
+ long baseRealtime) {
}
void detach() {
- mUnpluggables.remove(this);
+ mOnBatteryTimeBase.remove(this);
}
void readFromParcelLocked(Parcel in) {
@@ -4243,51 +5290,33 @@ public final class BatteryStatsImpl extends BatteryStats {
@Override
public int getLaunches(int which) {
- int val;
-
- if (which == STATS_LAST) {
- val = mLastLaunches;
- } else {
- val = mLaunches;
- if (which == STATS_CURRENT) {
- val -= mLoadedLaunches;
- } else if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedLaunches;
- }
+ int val = mLaunches;
+ if (which == STATS_CURRENT) {
+ val -= mLoadedLaunches;
+ } else if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedLaunches;
}
-
return val;
}
@Override
public long getStartTime(long now, int which) {
- long val;
- if (which == STATS_LAST) {
- val = mLastStartTime;
- } else {
- val = getStartTimeToNowLocked(now);
- if (which == STATS_CURRENT) {
- val -= mLoadedStartTime;
- } else if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedStartTime;
- }
+ long val = getStartTimeToNowLocked(now);
+ if (which == STATS_CURRENT) {
+ val -= mLoadedStartTime;
+ } else if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedStartTime;
}
-
return val;
}
@Override
public int getStarts(int which) {
- int val;
- if (which == STATS_LAST) {
- val = mLastStarts;
- } else {
- val = mStarts;
- if (which == STATS_CURRENT) {
- val -= mLoadedStarts;
- } else if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedStarts;
- }
+ int val = mStarts;
+ if (which == STATS_CURRENT) {
+ val -= mLoadedStarts;
+ } else if (which == STATS_SINCE_UNPLUGGED) {
+ val -= mUnpluggedStarts;
}
return val;
@@ -4382,7 +5411,7 @@ public final class BatteryStatsImpl extends BatteryStats {
t = wl.mTimerPartial;
if (t == null) {
t = new StopwatchTimer(Uid.this, WAKE_TYPE_PARTIAL,
- mPartialTimers, mUnpluggables);
+ mPartialTimers, mOnBatteryScreenOffTimeBase);
wl.mTimerPartial = t;
}
return t;
@@ -4390,7 +5419,7 @@ public final class BatteryStatsImpl extends BatteryStats {
t = wl.mTimerFull;
if (t == null) {
t = new StopwatchTimer(Uid.this, WAKE_TYPE_FULL,
- mFullTimers, mUnpluggables);
+ mFullTimers, mOnBatteryTimeBase);
wl.mTimerFull = t;
}
return t;
@@ -4398,7 +5427,7 @@ public final class BatteryStatsImpl extends BatteryStats {
t = wl.mTimerWindow;
if (t == null) {
t = new StopwatchTimer(Uid.this, WAKE_TYPE_WINDOW,
- mWindowTimers, mUnpluggables);
+ mWindowTimers, mOnBatteryTimeBase);
wl.mTimerWindow = t;
}
return t;
@@ -4425,34 +5454,36 @@ public final class BatteryStatsImpl extends BatteryStats {
timers = new ArrayList<StopwatchTimer>();
mSensorTimers.put(sensor, timers);
}
- t = new StopwatchTimer(Uid.this, BatteryStats.SENSOR, timers, mUnpluggables);
+ t = new StopwatchTimer(Uid.this, BatteryStats.SENSOR, timers, mOnBatteryTimeBase);
se.mTimer = t;
return t;
}
- public void noteStartWakeLocked(int pid, String name, int type) {
+ public void noteStartWakeLocked(int pid, String name, int type, long elapsedRealtimeMs) {
StopwatchTimer t = getWakeTimerLocked(name, type);
if (t != null) {
- t.startRunningLocked(BatteryStatsImpl.this);
+ t.startRunningLocked(elapsedRealtimeMs);
}
if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
Pid p = getPidStatsLocked(pid);
- if (p.mWakeStart == 0) {
- p.mWakeStart = SystemClock.elapsedRealtime();
+ if (p.mWakeNesting++ == 0) {
+ p.mWakeStartMs = elapsedRealtimeMs;
}
}
}
- public void noteStopWakeLocked(int pid, String name, int type) {
+ public void noteStopWakeLocked(int pid, String name, int type, long elapsedRealtimeMs) {
StopwatchTimer t = getWakeTimerLocked(name, type);
if (t != null) {
- t.stopRunningLocked(BatteryStatsImpl.this);
+ t.stopRunningLocked(elapsedRealtimeMs);
}
if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
Pid p = mPids.get(pid);
- if (p != null && p.mWakeStart != 0) {
- p.mWakeSum += SystemClock.elapsedRealtime() - p.mWakeStart;
- p.mWakeStart = 0;
+ if (p != null && p.mWakeNesting > 0) {
+ if (p.mWakeNesting-- == 1) {
+ p.mWakeSumMs += elapsedRealtimeMs - p.mWakeStartMs;
+ p.mWakeStartMs = 0;
+ }
}
}
}
@@ -4471,32 +5502,32 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- public void noteStartSensor(int sensor) {
+ public void noteStartSensor(int sensor, long elapsedRealtimeMs) {
StopwatchTimer t = getSensorTimerLocked(sensor, true);
if (t != null) {
- t.startRunningLocked(BatteryStatsImpl.this);
+ t.startRunningLocked(elapsedRealtimeMs);
}
}
- public void noteStopSensor(int sensor) {
+ public void noteStopSensor(int sensor, long elapsedRealtimeMs) {
// Don't create a timer if one doesn't already exist
StopwatchTimer t = getSensorTimerLocked(sensor, false);
if (t != null) {
- t.stopRunningLocked(BatteryStatsImpl.this);
+ t.stopRunningLocked(elapsedRealtimeMs);
}
}
- public void noteStartGps() {
+ public void noteStartGps(long elapsedRealtimeMs) {
StopwatchTimer t = getSensorTimerLocked(Sensor.GPS, true);
if (t != null) {
- t.startRunningLocked(BatteryStatsImpl.this);
+ t.startRunningLocked(elapsedRealtimeMs);
}
}
- public void noteStopGps() {
+ public void noteStopGps(long elapsedRealtimeMs) {
StopwatchTimer t = getSensorTimerLocked(Sensor.GPS, false);
if (t != null) {
- t.stopRunningLocked(BatteryStatsImpl.this);
+ t.stopRunningLocked(elapsedRealtimeMs);
}
}
@@ -4509,38 +5540,51 @@ public final class BatteryStatsImpl extends BatteryStats {
mFile = new JournaledFile(new File(filename), new File(filename + ".tmp"));
mHandler = new MyHandler(handler.getLooper());
mStartCount++;
- mScreenOnTimer = new StopwatchTimer(null, -1, null, mUnpluggables);
+ mScreenOnTimer = new StopwatchTimer(null, -1, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mUnpluggables);
+ mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mOnBatteryTimeBase);
}
- mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables);
+ mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
+ mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase);
+ mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
- mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null, mUnpluggables);
+ mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null,
+ mOnBatteryTimeBase);
}
- mPhoneSignalScanningTimer = new StopwatchTimer(null, -200+1, null, mUnpluggables);
+ mPhoneSignalScanningTimer = new StopwatchTimer(null, -200+1, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i, null, mUnpluggables);
+ mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i, null,
+ mOnBatteryTimeBase);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables);
- }
- mWifiOnTimer = new StopwatchTimer(null, -3, null, mUnpluggables);
- mGlobalWifiRunningTimer = new StopwatchTimer(null, -4, null, mUnpluggables);
- mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mUnpluggables);
- mAudioOnTimer = new StopwatchTimer(null, -6, null, mUnpluggables);
- mVideoOnTimer = new StopwatchTimer(null, -7, null, mUnpluggables);
- mInteractiveTimer = new StopwatchTimer(null, -8, null, mUnpluggables);
+ mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
+ mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
+ }
+ mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase);
+ mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase);
+ mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
+ mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase);
+ mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase);
+ mWifiOnTimer = new StopwatchTimer(null, -4, null, mOnBatteryTimeBase);
+ mGlobalWifiRunningTimer = new StopwatchTimer(null, -5, null, mOnBatteryTimeBase);
+ for (int i=0; i<NUM_WIFI_STATES; i++) {
+ mWifiStateTimer[i] = new StopwatchTimer(null, -600-i, null, mOnBatteryTimeBase);
+ }
+ mBluetoothOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase);
+ for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
+ mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i, null, mOnBatteryTimeBase);
+ }
+ mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
+ mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
mOnBattery = mOnBatteryInternal = false;
- initTimes();
- mTrackBatteryPastUptime = 0;
- mTrackBatteryPastRealtime = 0;
- mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000;
- mRealtimeStart = mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime() * 1000;
- mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart);
- mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart);
+ long uptime = SystemClock.uptimeMillis() * 1000;
+ long realtime = SystemClock.elapsedRealtime() * 1000;
+ initTimes(uptime, realtime);
mDischargeStartLevel = 0;
mDischargeUnplugLevel = 0;
+ mDischargePlugLevel = -1;
mDischargeCurrentLevel = 0;
+ mCurrentBatteryLevel = 0;
initDischarge();
clearHistoryLocked();
}
@@ -4570,18 +5614,21 @@ public final class BatteryStatsImpl extends BatteryStats {
public boolean startIteratingOldHistoryLocked() {
if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
+ " pos=" + mHistoryBuffer.dataPosition());
+ if ((mHistoryIterator = mHistory) == null) {
+ return false;
+ }
mHistoryBuffer.setDataPosition(0);
mHistoryReadTmp.clear();
mReadOverflow = false;
mIteratingHistory = true;
- return (mHistoryIterator = mHistory) != null;
+ return true;
}
@Override
public boolean getNextOldHistoryLocked(HistoryItem out) {
boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize();
if (!end) {
- mHistoryReadTmp.readDelta(mHistoryBuffer);
+ readHistoryDelta(mHistoryBuffer, mHistoryReadTmp);
mReadOverflow |= mHistoryReadTmp.cmd == HistoryItem.CMD_OVERFLOW;
}
HistoryItem cur = mHistoryIterator;
@@ -4597,13 +5644,13 @@ public final class BatteryStatsImpl extends BatteryStats {
if (end) {
Slog.w(TAG, "New history ends before old history!");
} else if (!out.same(mHistoryReadTmp)) {
- long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG));
pw.println("Histories differ!");
pw.println("Old history:");
- (new HistoryPrinter()).printNextItem(pw, out, now);
+ (new HistoryPrinter()).printNextItem(pw, out, 0, false, true);
pw.println("New history:");
- (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now);
+ (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, 0, false,
+ true);
pw.flush();
}
}
@@ -4614,16 +5661,60 @@ public final class BatteryStatsImpl extends BatteryStats {
public void finishIteratingOldHistoryLocked() {
mIteratingHistory = false;
mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+ mHistoryIterator = null;
+ }
+
+ public int getHistoryTotalSize() {
+ return MAX_HISTORY_BUFFER;
+ }
+
+ public int getHistoryUsedSize() {
+ return mHistoryBuffer.dataSize();
}
@Override
public boolean startIteratingHistoryLocked() {
if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
+ " pos=" + mHistoryBuffer.dataPosition());
+ if (mHistoryBuffer.dataSize() <= 0) {
+ return false;
+ }
mHistoryBuffer.setDataPosition(0);
mReadOverflow = false;
mIteratingHistory = true;
- return mHistoryBuffer.dataSize() > 0;
+ mReadHistoryStrings = new String[mHistoryTagPool.size()];
+ mReadHistoryUids = new int[mHistoryTagPool.size()];
+ mReadHistoryChars = 0;
+ for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
+ final HistoryTag tag = ent.getKey();
+ final int idx = ent.getValue();
+ mReadHistoryStrings[idx] = tag.string;
+ mReadHistoryUids[idx] = tag.uid;
+ mReadHistoryChars += tag.string.length() + 1;
+ }
+ return true;
+ }
+
+ @Override
+ public int getHistoryStringPoolSize() {
+ return mReadHistoryStrings.length;
+ }
+
+ @Override
+ public int getHistoryStringPoolBytes() {
+ // Each entry is a fixed 12 bytes: 4 for index, 4 for uid, 4 for string size
+ // Each string character is 2 bytes.
+ return (mReadHistoryStrings.length * 12) + (mReadHistoryChars * 2);
+ }
+
+ @Override
+ public String getHistoryTagPoolString(int index) {
+ return mReadHistoryStrings[index];
+ }
+
+ @Override
+ public int getHistoryTagPoolUid(int index) {
+ return mReadHistoryUids[index];
}
@Override
@@ -4637,7 +5728,13 @@ public final class BatteryStatsImpl extends BatteryStats {
return false;
}
- out.readDelta(mHistoryBuffer);
+ final long lastRealtime = out.time;
+ final long lastWalltime = out.currentTime;
+ readHistoryDelta(mHistoryBuffer, out);
+ if (out.cmd != HistoryItem.CMD_CURRENT_TIME
+ && out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) {
+ out.currentTime = lastWalltime + (out.time - lastRealtime);
+ }
return true;
}
@@ -4645,6 +5742,7 @@ public final class BatteryStatsImpl extends BatteryStats {
public void finishIteratingHistoryLocked() {
mIteratingHistory = false;
mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+ mReadHistoryStrings = null;
}
@Override
@@ -4665,13 +5763,14 @@ public final class BatteryStatsImpl extends BatteryStats {
return mScreenState == Display.STATE_ON;
}
- void initTimes() {
- mBatteryRealtime = mTrackBatteryPastUptime = 0;
- mBatteryUptime = mTrackBatteryPastRealtime = 0;
- mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000;
- mRealtimeStart = mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime() * 1000;
- mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart);
- mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart);
+ void initTimes(long uptime, long realtime) {
+ mStartClockTime = System.currentTimeMillis();
+ mOnBatteryTimeBase.init(uptime, realtime);
+ mOnBatteryScreenOffTimeBase.init(uptime, realtime);
+ mRealtime = 0;
+ mUptime = 0;
+ mRealtimeStart = realtime;
+ mUptimeStart = uptime;
}
void initDischarge() {
@@ -4681,32 +5780,76 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeAmountScreenOnSinceCharge = 0;
mDischargeAmountScreenOff = 0;
mDischargeAmountScreenOffSinceCharge = 0;
+ mLastDischargeStepTime = -1;
+ mNumDischargeStepDurations = 0;
+ mLastChargeStepTime = -1;
+ mNumChargeStepDurations = 0;
}
-
- public void resetAllStatsLocked() {
+
+ public void resetAllStatsCmdLocked() {
+ resetAllStatsLocked();
+ final long mSecUptime = SystemClock.uptimeMillis();
+ long uptime = mSecUptime * 1000;
+ long mSecRealtime = SystemClock.elapsedRealtime();
+ long realtime = mSecRealtime * 1000;
+ mDischargeStartLevel = mHistoryCur.batteryLevel;
+ pullPendingStateUpdatesLocked();
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
+ mDischargeCurrentLevel = mDischargeUnplugLevel = mDischargePlugLevel
+ = mCurrentBatteryLevel = mHistoryCur.batteryLevel;
+ mOnBatteryTimeBase.reset(uptime, realtime);
+ mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
+ if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
+ if (mScreenState == Display.STATE_ON) {
+ mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else {
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenOffUnplugLevel = mHistoryCur.batteryLevel;
+ }
+ mDischargeAmountScreenOn = 0;
+ mDischargeAmountScreenOff = 0;
+ }
+ initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
+ }
+
+ private void resetAllStatsLocked() {
mStartCount = 0;
- initTimes();
- mScreenOnTimer.reset(this, false);
+ initTimes(SystemClock.uptimeMillis() * 1000, SystemClock.elapsedRealtime() * 1000);
+ mScreenOnTimer.reset(false);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i].reset(this, false);
+ mScreenBrightnessTimer[i].reset(false);
}
- mInteractiveTimer.reset(this, false);
- mPhoneOnTimer.reset(this, false);
- mAudioOnTimer.reset(this, false);
- mVideoOnTimer.reset(this, false);
+ mInteractiveTimer.reset(false);
+ mLowPowerModeEnabledTimer.reset(false);
+ mPhoneOnTimer.reset(false);
+ mAudioOnTimer.reset(false);
+ mVideoOnTimer.reset(false);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
- mPhoneSignalStrengthsTimer[i].reset(this, false);
+ mPhoneSignalStrengthsTimer[i].reset(false);
}
- mPhoneSignalScanningTimer.reset(this, false);
+ mPhoneSignalScanningTimer.reset(false);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i].reset(this, false);
+ mPhoneDataConnectionsTimer[i].reset(false);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i].reset(false);
+ mNetworkByteActivityCounters[i].reset(false);
+ mNetworkPacketActivityCounters[i].reset(false);
+ }
+ mMobileRadioActiveTimer.reset(false);
+ mMobileRadioActivePerAppTimer.reset(false);
+ mMobileRadioActiveAdjustedTime.reset(false);
+ mMobileRadioActiveUnknownTime.reset(false);
+ mMobileRadioActiveUnknownCount.reset(false);
+ mWifiOnTimer.reset(false);
+ mGlobalWifiRunningTimer.reset(false);
+ for (int i=0; i<NUM_WIFI_STATES; i++) {
+ mWifiStateTimer[i].reset(false);
+ }
+ mBluetoothOnTimer.reset(false);
+ for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
+ mBluetoothStateTimer[i].reset(false);
}
- mWifiOnTimer.reset(this, false);
- mGlobalWifiRunningTimer.reset(this, false);
- mBluetoothOnTimer.reset(this, false);
for (int i=0; i<mUidStats.size(); i++) {
if (mUidStats.valueAt(i).reset()) {
@@ -4717,16 +5860,39 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mKernelWakelockStats.size() > 0) {
for (SamplingTimer timer : mKernelWakelockStats.values()) {
- mUnpluggables.remove(timer);
+ mOnBatteryScreenOffTimeBase.remove(timer);
}
mKernelWakelockStats.clear();
}
-
+
+ if (mWakeupReasonStats.size() > 0) {
+ for (LongSamplingCounter timer : mWakeupReasonStats.values()) {
+ mOnBatteryScreenOffTimeBase.remove(timer);
+ }
+ mWakeupReasonStats.clear();
+ }
+
initDischarge();
clearHistoryLocked();
}
+ private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
+ for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
+ HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i);
+ if (active == null) {
+ continue;
+ }
+ for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
+ SparseIntArray uids = ent.getValue();
+ for (int j=0; j<uids.size(); j++) {
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+ uids.keyAt(j));
+ }
+ }
+ }
+ }
+
void updateDischargeScreenLevelsLocked(boolean oldScreenOn, boolean newScreenOn) {
if (oldScreenOn) {
int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel;
@@ -4750,46 +5916,54 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- void setOnBattery(boolean onBattery, int oldStatus, int level) {
- synchronized(this) {
- setOnBatteryLocked(onBattery, oldStatus, level);
+ public void pullPendingStateUpdatesLocked() {
+ updateKernelWakelocksLocked();
+ updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime());
+ if (mOnBatteryInternal) {
+ final boolean screenOn = mScreenState == Display.STATE_ON;
+ updateDischargeScreenLevelsLocked(screenOn, screenOn);
}
}
- void setOnBatteryLocked(boolean onBattery, int oldStatus, int level) {
+ void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
+ final int oldStatus, final int level) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
mHandler.sendMessage(m);
mOnBattery = mOnBatteryInternal = onBattery;
- long uptime = SystemClock.uptimeMillis() * 1000;
- long mSecRealtime = SystemClock.elapsedRealtime();
- long realtime = mSecRealtime * 1000;
+ final long uptime = mSecUptime * 1000;
+ final long realtime = mSecRealtime * 1000;
final boolean screenOn = mScreenState == Display.STATE_ON;
if (onBattery) {
// We will reset our status if we are unplugging after the
// battery was last full, or the level is at 100, or
// we have gone through a significant charge (from a very low
// level to a now very high level).
- if (oldStatus == BatteryManager.BATTERY_STATUS_FULL
+ boolean reset = false;
+ if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
|| level >= 90
- || (mDischargeCurrentLevel < 20 && level >= 80)) {
+ || (mDischargeCurrentLevel < 20 && level >= 80))) {
doWrite = true;
resetAllStatsLocked();
mDischargeStartLevel = level;
+ reset = true;
+ mNumDischargeStepDurations = 0;
}
- updateKernelWakelocksLocked();
- updateNetworkActivityLocked();
+ mLastDischargeStepLevel = level;
+ mMinDischargeStepLevel = level;
+ mLastDischargeStepTime = -1;
+ pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(mSecRealtime);
- mTrackBatteryUptimeStart = uptime;
- mTrackBatteryRealtimeStart = realtime;
- mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime);
- mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(realtime);
+ if (reset) {
+ mRecordingHistory = true;
+ startRecordingHistory(mSecRealtime, mSecUptime, reset);
+ }
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
if (screenOn) {
mDischargeScreenOnUnplugLevel = level;
@@ -4800,24 +5974,25 @@ public final class BatteryStatsImpl extends BatteryStats {
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
- doUnplugLocked(realtime, mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
+ updateTimeBasesLocked(true, !screenOn, uptime, realtime);
} else {
- updateKernelWakelocksLocked();
- updateNetworkActivityLocked();
+ pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(mSecRealtime);
- mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart;
- mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
- mDischargeCurrentLevel = level;
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
+ mDischargeCurrentLevel = mDischargePlugLevel = level;
if (level < mDischargeUnplugLevel) {
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
}
updateDischargeScreenLevelsLocked(screenOn, screenOn);
- doPlugLocked(realtime, getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
+ updateTimeBasesLocked(false, !screenOn, uptime, realtime);
+ mNumChargeStepDurations = 0;
+ mLastChargeStepLevel = level;
+ mMaxChargeStepLevel = level;
+ mLastChargeStepTime = -1;
}
if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
if (mFile != null) {
@@ -4826,13 +6001,46 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
+ boolean reset) {
+ mRecordingHistory = true;
+ mHistoryCur.currentTime = System.currentTimeMillis();
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs,
+ reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
+ mHistoryCur);
+ mHistoryCur.currentTime = 0;
+ if (reset) {
+ initActiveHistoryEventsLocked(elapsedRealtimeMs, uptimeMs);
+ }
+ }
+
// This should probably be exposed in the API, though it's not critical
private static final int BATTERY_PLUGGED_NONE = 0;
+ private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime,
+ int numStepLevels, long elapsedRealtime) {
+ if (lastStepTime >= 0 && numStepLevels > 0) {
+ long duration = elapsedRealtime - lastStepTime;
+ for (int i=0; i<numStepLevels; i++) {
+ System.arraycopy(steps, 0, steps, 1, steps.length-1);
+ long thisDuration = duration / (numStepLevels-i);
+ duration -= thisDuration;
+ steps[0] = thisDuration;
+ }
+ stepCount += numStepLevels;
+ if (stepCount > steps.length) {
+ stepCount = steps.length;
+ }
+ }
+ return stepCount;
+ }
+
public void setBatteryState(int status, int health, int plugType, int level,
int temp, int volt) {
synchronized(this) {
- boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+ final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+ final long uptime = SystemClock.uptimeMillis();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
int oldStatus = mHistoryCur.batteryStatus;
if (!mHaveBatteryLevel) {
mHaveBatteryLevel = true;
@@ -4851,7 +6059,19 @@ public final class BatteryStatsImpl extends BatteryStats {
}
if (onBattery) {
mDischargeCurrentLevel = level;
- mRecordingHistory = true;
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtime, uptime, true);
+ }
+ } else if (level < 96) {
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtime, uptime, true);
+ }
+ }
+ mCurrentBatteryLevel = level;
+ if (mDischargePlugLevel < 0) {
+ mDischargePlugLevel = level;
}
if (onBattery != mOnBattery) {
mHistoryCur.batteryLevel = (byte)level;
@@ -4860,7 +6080,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mHistoryCur.batteryPlugType = (byte)plugType;
mHistoryCur.batteryTemperature = (short)temp;
mHistoryCur.batteryVoltage = (char)volt;
- setOnBatteryLocked(onBattery, oldStatus, level);
+ setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
} else {
boolean changed = false;
if (mHistoryCur.batteryLevel != level) {
@@ -4890,13 +6110,32 @@ public final class BatteryStatsImpl extends BatteryStats {
changed = true;
}
if (changed) {
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+ if (onBattery) {
+ if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
+ mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations,
+ mNumDischargeStepDurations, mLastDischargeStepTime,
+ mLastDischargeStepLevel - level, elapsedRealtime);
+ mLastDischargeStepLevel = level;
+ mMinDischargeStepLevel = level;
+ mLastDischargeStepTime = elapsedRealtime;
+ }
+ } else {
+ if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
+ mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,
+ mNumChargeStepDurations, mLastChargeStepTime,
+ level - mLastChargeStepLevel, elapsedRealtime);
+ mLastChargeStepLevel = level;
+ mMaxChargeStepLevel = level;
+ mLastChargeStepTime = elapsedRealtime;
+ }
}
}
if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
// We don't record history while we are plugged in and fully charged.
// The next time we are unplugged, history will be cleared.
- mRecordingHistory = false;
+ mRecordingHistory = DEBUG;
}
}
}
@@ -4916,8 +6155,8 @@ public final class BatteryStatsImpl extends BatteryStats {
SamplingTimer kwlt = mKernelWakelockStats.get(name);
if (kwlt == null) {
- kwlt = new SamplingTimer(mUnpluggables, mOnBatteryInternal,
- true /* track reported values */);
+ kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase,
+ true /* track reported val */);
mKernelWakelockStats.put(name, kwlt);
}
kwlt.updateCurrentReportedCount(kws.mCount);
@@ -4936,48 +6175,124 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- private void updateNetworkActivityLocked() {
+ static final int NET_UPDATE_MOBILE = 1<<0;
+ static final int NET_UPDATE_WIFI = 1<<1;
+ static final int NET_UPDATE_ALL = 0xffff;
+
+ private void updateNetworkActivityLocked(int which, long elapsedRealtimeMs) {
if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return;
- final NetworkStats snapshot;
- try {
- snapshot = mNetworkStatsFactory.readNetworkStatsDetail();
- } catch (IOException e) {
- Log.wtf(TAG, "Failed to read network stats", e);
- return;
- }
+ if ((which&NET_UPDATE_MOBILE) != 0 && mMobileIfaces.length > 0) {
+ final NetworkStats snapshot;
+ final NetworkStats last = mCurMobileSnapshot;
+ try {
+ snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL,
+ mMobileIfaces, NetworkStats.TAG_NONE, mLastMobileSnapshot);
+ } catch (IOException e) {
+ Log.wtf(TAG, "Failed to read mobile network stats", e);
+ return;
+ }
- if (mLastSnapshot == null) {
- mLastSnapshot = snapshot;
- return;
+ mCurMobileSnapshot = snapshot;
+ mLastMobileSnapshot = last;
+
+ if (mOnBatteryInternal) {
+ final NetworkStats delta = NetworkStats.subtract(snapshot, last,
+ null, null, mTmpNetworkStats);
+ mTmpNetworkStats = delta;
+
+ long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(
+ elapsedRealtimeMs);
+ long totalPackets = delta.getTotalPackets();
+
+ final int size = delta.size();
+ for (int i = 0; i < size; i++) {
+ final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+
+ if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
+
+ final Uid u = getUidStatsLocked(mapUid(entry.uid));
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
+ entry.rxPackets);
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes,
+ entry.txPackets);
+
+ if (radioTime > 0) {
+ // Distribute total radio active time in to this app.
+ long appPackets = entry.rxPackets + entry.txPackets;
+ long appRadioTime = (radioTime*appPackets)/totalPackets;
+ u.noteMobileRadioActiveTimeLocked(appRadioTime);
+ // Remove this app from the totals, so that we don't lose any time
+ // due to rounding.
+ radioTime -= appRadioTime;
+ totalPackets -= appPackets;
+ }
+
+ mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+ entry.rxBytes);
+ mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+ entry.txBytes);
+ mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+ entry.rxPackets);
+ mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+ entry.txPackets);
+ }
+
+ if (radioTime > 0) {
+ // Whoops, there is some radio time we can't blame on an app!
+ mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
+ mMobileRadioActiveUnknownCount.addCountLocked(1);
+ }
+ }
}
- final NetworkStats delta = snapshot.subtract(mLastSnapshot);
- mLastSnapshot = snapshot;
+ if ((which&NET_UPDATE_WIFI) != 0 && mWifiIfaces.length > 0) {
+ final NetworkStats snapshot;
+ final NetworkStats last = mCurWifiSnapshot;
+ try {
+ snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL,
+ mWifiIfaces, NetworkStats.TAG_NONE, mLastWifiSnapshot);
+ } catch (IOException e) {
+ Log.wtf(TAG, "Failed to read wifi network stats", e);
+ return;
+ }
- NetworkStats.Entry entry = null;
- final int size = delta.size();
- for (int i = 0; i < size; i++) {
- entry = delta.getValues(i, entry);
+ mCurWifiSnapshot = snapshot;
+ mLastWifiSnapshot = last;
- if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
- if (entry.tag != NetworkStats.TAG_NONE) continue;
+ if (mOnBatteryInternal) {
+ final NetworkStats delta = NetworkStats.subtract(snapshot, last,
+ null, null, mTmpNetworkStats);
+ mTmpNetworkStats = delta;
- final Uid u = getUidStatsLocked(entry.uid);
+ final int size = delta.size();
+ for (int i = 0; i < size; i++) {
+ final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
- if (mMobileIfaces.contains(entry.iface)) {
- u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_BYTES, entry.rxBytes);
- u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_BYTES, entry.txBytes);
+ if (DEBUG) {
+ final NetworkStats.Entry cur = snapshot.getValues(i, null);
+ Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
+ + " tx=" + entry.txBytes + ", cur rx=" + cur.rxBytes
+ + " tx=" + cur.txBytes);
+ }
- mNetworkActivityCounters[NETWORK_MOBILE_RX_BYTES].addCountLocked(entry.rxBytes);
- mNetworkActivityCounters[NETWORK_MOBILE_TX_BYTES].addCountLocked(entry.txBytes);
+ if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
- } else if (mWifiIfaces.contains(entry.iface)) {
- u.noteNetworkActivityLocked(NETWORK_WIFI_RX_BYTES, entry.rxBytes);
- u.noteNetworkActivityLocked(NETWORK_WIFI_TX_BYTES, entry.txBytes);
+ final Uid u = getUidStatsLocked(mapUid(entry.uid));
+ u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
+ entry.rxPackets);
+ u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
+ entry.txPackets);
- mNetworkActivityCounters[NETWORK_WIFI_RX_BYTES].addCountLocked(entry.rxBytes);
- mNetworkActivityCounters[NETWORK_WIFI_TX_BYTES].addCountLocked(entry.txBytes);
+ mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+ entry.rxBytes);
+ mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+ entry.txBytes);
+ mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+ entry.rxPackets);
+ mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+ entry.txPackets);
+ }
}
}
}
@@ -4994,9 +6309,8 @@ public final class BatteryStatsImpl extends BatteryStats {
public long computeUptime(long curTime, int which) {
switch (which) {
case STATS_SINCE_CHARGED: return mUptime + (curTime-mUptimeStart);
- case STATS_LAST: return mLastUptime;
case STATS_CURRENT: return (curTime-mUptimeStart);
- case STATS_SINCE_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart);
+ case STATS_SINCE_UNPLUGGED: return (curTime-mOnBatteryTimeBase.getUptimeStart());
}
return 0;
}
@@ -5005,71 +6319,155 @@ public final class BatteryStatsImpl extends BatteryStats {
public long computeRealtime(long curTime, int which) {
switch (which) {
case STATS_SINCE_CHARGED: return mRealtime + (curTime-mRealtimeStart);
- case STATS_LAST: return mLastRealtime;
case STATS_CURRENT: return (curTime-mRealtimeStart);
- case STATS_SINCE_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart);
+ case STATS_SINCE_UNPLUGGED: return (curTime-mOnBatteryTimeBase.getRealtimeStart());
}
return 0;
}
@Override
public long computeBatteryUptime(long curTime, int which) {
- switch (which) {
- case STATS_SINCE_CHARGED:
- return mBatteryUptime + getBatteryUptime(curTime);
- case STATS_LAST:
- return mBatteryLastUptime;
- case STATS_CURRENT:
- return getBatteryUptime(curTime);
- case STATS_SINCE_UNPLUGGED:
- return getBatteryUptimeLocked(curTime) - mUnpluggedBatteryUptime;
- }
- return 0;
+ return mOnBatteryTimeBase.computeUptime(curTime, which);
}
@Override
public long computeBatteryRealtime(long curTime, int which) {
- switch (which) {
- case STATS_SINCE_CHARGED:
- return mBatteryRealtime + getBatteryRealtimeLocked(curTime);
- case STATS_LAST:
- return mBatteryLastRealtime;
- case STATS_CURRENT:
- return getBatteryRealtimeLocked(curTime);
- case STATS_SINCE_UNPLUGGED:
- return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime;
+ return mOnBatteryTimeBase.computeRealtime(curTime, which);
+ }
+
+ @Override
+ public long computeBatteryScreenOffUptime(long curTime, int which) {
+ return mOnBatteryScreenOffTimeBase.computeUptime(curTime, which);
+ }
+
+ @Override
+ public long computeBatteryScreenOffRealtime(long curTime, int which) {
+ return mOnBatteryScreenOffTimeBase.computeRealtime(curTime, which);
+ }
+
+ private long computeTimePerLevel(long[] steps, int numSteps) {
+ // For now we'll do a simple average across all steps.
+ if (numSteps <= 0) {
+ return -1;
}
- return 0;
+ long total = 0;
+ for (int i=0; i<numSteps; i++) {
+ total += steps[i];
+ }
+ return total / numSteps;
+ /*
+ long[] buckets = new long[numSteps];
+ int numBuckets = 0;
+ int numToAverage = 4;
+ int i = 0;
+ while (i < numSteps) {
+ long totalTime = 0;
+ int num = 0;
+ for (int j=0; j<numToAverage && (i+j)<numSteps; j++) {
+ totalTime += steps[i+j];
+ num++;
+ }
+ buckets[numBuckets] = totalTime / num;
+ numBuckets++;
+ numToAverage *= 2;
+ i += num;
+ }
+ if (numBuckets < 1) {
+ return -1;
+ }
+ long averageTime = buckets[numBuckets-1];
+ for (i=numBuckets-2; i>=0; i--) {
+ averageTime = (averageTime + buckets[i]) / 2;
+ }
+ return averageTime;
+ */
}
- long getBatteryUptimeLocked(long curTime) {
- long time = mTrackBatteryPastUptime;
- if (mOnBatteryInternal) {
- time += curTime - mTrackBatteryUptimeStart;
+ @Override
+ public long computeBatteryTimeRemaining(long curTime) {
+ if (!mOnBattery) {
+ return -1;
}
- return time;
+ /* Simple implementation just looks at the average discharge per level across the
+ entire sample period.
+ int discharge = (getLowDischargeAmountSinceCharge()+getHighDischargeAmountSinceCharge())/2;
+ if (discharge < 2) {
+ return -1;
+ }
+ long duration = computeBatteryRealtime(curTime, STATS_SINCE_CHARGED);
+ if (duration < 1000*1000) {
+ return -1;
+ }
+ long usPerLevel = duration/discharge;
+ return usPerLevel * mCurrentBatteryLevel;
+ */
+ if (mNumDischargeStepDurations < 1) {
+ return -1;
+ }
+ long msPerLevel = computeTimePerLevel(mDischargeStepDurations, mNumDischargeStepDurations);
+ if (msPerLevel <= 0) {
+ return -1;
+ }
+ return (msPerLevel * mCurrentBatteryLevel) * 1000;
}
- long getBatteryUptimeLocked() {
- return getBatteryUptime(SystemClock.uptimeMillis() * 1000);
+ public int getNumDischargeStepDurations() {
+ return mNumDischargeStepDurations;
}
- @Override
- public long getBatteryUptime(long curTime) {
- return getBatteryUptimeLocked(curTime);
+ public long[] getDischargeStepDurationsArray() {
+ return mDischargeStepDurations;
}
- long getBatteryRealtimeLocked(long curTime) {
- long time = mTrackBatteryPastRealtime;
- if (mOnBatteryInternal) {
- time += curTime - mTrackBatteryRealtimeStart;
+ @Override
+ public long computeChargeTimeRemaining(long curTime) {
+ if (mOnBattery) {
+ // Not yet working.
+ return -1;
+ }
+ /* Broken
+ int curLevel = mCurrentBatteryLevel;
+ int plugLevel = mDischargePlugLevel;
+ if (plugLevel < 0 || curLevel < (plugLevel+1)) {
+ return -1;
}
- return time;
+ long duration = computeBatteryRealtime(curTime, STATS_SINCE_UNPLUGGED);
+ if (duration < 1000*1000) {
+ return -1;
+ }
+ long usPerLevel = duration/(curLevel-plugLevel);
+ return usPerLevel * (100-curLevel);
+ */
+ if (mNumChargeStepDurations < 1) {
+ return -1;
+ }
+ long msPerLevel = computeTimePerLevel(mChargeStepDurations, mNumChargeStepDurations);
+ if (msPerLevel <= 0) {
+ return -1;
+ }
+ return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;
+ }
+
+ public int getNumChargeStepDurations() {
+ return mNumChargeStepDurations;
+ }
+
+ public long[] getChargeStepDurationsArray() {
+ return mChargeStepDurations;
+ }
+
+ long getBatteryUptimeLocked() {
+ return mOnBatteryTimeBase.getUptime(SystemClock.uptimeMillis() * 1000);
+ }
+
+ @Override
+ public long getBatteryUptime(long curTime) {
+ return mOnBatteryTimeBase.getUptime(curTime);
}
@Override
public long getBatteryRealtime(long curTime) {
- return getBatteryRealtimeLocked(curTime);
+ return mOnBatteryTimeBase.getRealtime(curTime);
}
@Override
@@ -5115,7 +6513,18 @@ public final class BatteryStatsImpl extends BatteryStats {
return val;
}
}
-
+
+ @Override
+ public int getDischargeAmount(int which) {
+ int dischargeAmount = which == STATS_SINCE_CHARGED
+ ? getHighDischargeAmountSinceCharge()
+ : (getDischargeStartLevel() - getDischargeCurrentLevel());
+ if (dischargeAmount < 0) {
+ dischargeAmount = 0;
+ }
+ return dischargeAmount;
+ }
+
public int getDischargeAmountScreenOn() {
synchronized(this) {
int val = mDischargeAmountScreenOn;
@@ -5189,24 +6598,7 @@ public final class BatteryStatsImpl extends BatteryStats {
* if needed.
*/
public Uid.Proc getProcessStatsLocked(int uid, String name) {
- Uid u = getUidStatsLocked(uid);
- return u.getProcessStatsLocked(name);
- }
-
- /**
- * Retrieve the statistics object for a particular process, given
- * the name of the process.
- * @param name process name
- * @return the statistics object for the process
- */
- public Uid.Proc getProcessStatsLocked(String name, int pid) {
- int uid;
- if (mUidCache.containsKey(name)) {
- uid = mUidCache.get(name);
- } else {
- uid = Process.getUidForPid(pid);
- mUidCache.put(name, uid);
- }
+ uid = mapUid(uid);
Uid u = getUidStatsLocked(uid);
return u.getProcessStatsLocked(name);
}
@@ -5216,6 +6608,7 @@ public final class BatteryStatsImpl extends BatteryStats {
* if needed.
*/
public Uid.Pkg getPackageStatsLocked(int uid, String pkg) {
+ uid = mapUid(uid);
Uid u = getUidStatsLocked(uid);
return u.getPackageStatsLocked(pkg);
}
@@ -5225,6 +6618,7 @@ public final class BatteryStatsImpl extends BatteryStats {
* if needed.
*/
public Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name) {
+ uid = mapUid(uid);
Uid u = getUidStatsLocked(uid);
return u.getServiceStatsLocked(pkg, name);
}
@@ -5265,7 +6659,7 @@ public final class BatteryStatsImpl extends BatteryStats {
time = (time*uidRunningTime)/totalRunningTime;
SamplingCounter uidSc = uidProc.mSpeedBins[sb];
if (uidSc == null) {
- uidSc = new SamplingCounter(mUnpluggables);
+ uidSc = new SamplingCounter(mOnBatteryTimeBase);
uidProc.mSpeedBins[sb] = uidSc;
}
uidSc.mCount.addAndGet((int)time);
@@ -5402,15 +6796,20 @@ public final class BatteryStatsImpl extends BatteryStats {
stream.close();
readSummaryFromParcel(in);
- } catch(java.io.IOException e) {
+ } catch(Exception e) {
Slog.e("BatteryStats", "Error reading battery statistics", e);
}
- long now = SystemClock.elapsedRealtime();
- if (USE_OLD_HISTORY) {
- addHistoryRecordLocked(now, HistoryItem.CMD_START);
+ if (mHistoryBuffer.dataPosition() > 0) {
+ mRecordingHistory = true;
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ if (USE_OLD_HISTORY) {
+ addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+ }
+ addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+ startRecordingHistory(elapsedRealtime, uptime, false);
}
- addHistoryBufferLocked(now, HistoryItem.CMD_START);
}
public int describeContents() {
@@ -5422,6 +6821,25 @@ public final class BatteryStatsImpl extends BatteryStats {
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
+ mHistoryTagPool.clear();
+ mNextHistoryTagIdx = 0;
+ mNumHistoryTagChars = 0;
+
+ int numTags = in.readInt();
+ for (int i=0; i<numTags; i++) {
+ int idx = in.readInt();
+ String str = in.readString();
+ int uid = in.readInt();
+ HistoryTag tag = new HistoryTag();
+ tag.string = str;
+ tag.uid = uid;
+ tag.poolIdx = idx;
+ mHistoryTagPool.put(tag, idx);
+ if (idx >= mNextHistoryTagIdx) {
+ mNextHistoryTagIdx = idx+1;
+ }
+ mNumHistoryTagChars += tag.string.length() + 1;
+ }
int bufSize = in.readInt();
int curPos = in.dataPosition();
@@ -5485,11 +6903,18 @@ public final class BatteryStatsImpl extends BatteryStats {
StringBuilder sb = new StringBuilder(128);
sb.append("****************** WRITING mHistoryBaseTime: ");
TimeUtils.formatDuration(mHistoryBaseTime, sb);
- sb.append(" mLastHistoryTime: ");
- TimeUtils.formatDuration(mLastHistoryTime, sb);
+ sb.append(" mLastHistoryElapsedRealtime: ");
+ TimeUtils.formatDuration(mLastHistoryElapsedRealtime, sb);
Slog.i(TAG, sb.toString());
}
- out.writeLong(mHistoryBaseTime + mLastHistoryTime);
+ out.writeLong(mHistoryBaseTime + mLastHistoryElapsedRealtime);
+ out.writeInt(mHistoryTagPool.size());
+ for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
+ HistoryTag tag = ent.getKey();
+ out.writeInt(ent.getValue());
+ out.writeString(tag.string);
+ out.writeInt(tag.uid);
+ }
out.writeInt(mHistoryBuffer.dataSize());
if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
+ mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
@@ -5523,16 +6948,23 @@ public final class BatteryStatsImpl extends BatteryStats {
readHistory(in, true);
mStartCount = in.readInt();
- mBatteryUptime = in.readLong();
- mBatteryRealtime = in.readLong();
mUptime = in.readLong();
mRealtime = in.readLong();
+ mStartClockTime = in.readLong();
+ mOnBatteryTimeBase.readSummaryFromParcel(in);
+ mOnBatteryScreenOffTimeBase.readSummaryFromParcel(in);
mDischargeUnplugLevel = in.readInt();
+ mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
+ mCurrentBatteryLevel = in.readInt();
mLowDischargeAmountSinceCharge = in.readInt();
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mNumDischargeStepDurations = in.readInt();
+ in.readLongArray(mDischargeStepDurations);
+ mNumChargeStepDurations = in.readInt();
+ in.readLongArray(mChargeStepDurations);
mStartCount++;
@@ -5544,6 +6976,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mInteractive = false;
mInteractiveTimer.readSummaryFromParcelLocked(in);
mPhoneOn = false;
+ mLowPowerModeEnabledTimer.readSummaryFromParcelLocked(in);
mPhoneOnTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
@@ -5553,14 +6986,27 @@ public final class BatteryStatsImpl extends BatteryStats {
mPhoneDataConnectionsTimer[i].readSummaryFromParcelLocked(in);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i].readSummaryFromParcelLocked(in);
- }
+ mNetworkByteActivityCounters[i].readSummaryFromParcelLocked(in);
+ mNetworkPacketActivityCounters[i].readSummaryFromParcelLocked(in);
+ }
+ mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ mMobileRadioActiveTimer.readSummaryFromParcelLocked(in);
+ mMobileRadioActivePerAppTimer.readSummaryFromParcelLocked(in);
+ mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in);
+ mMobileRadioActiveUnknownTime.readSummaryFromParcelLocked(in);
+ mMobileRadioActiveUnknownCount.readSummaryFromParcelLocked(in);
mWifiOn = false;
mWifiOnTimer.readSummaryFromParcelLocked(in);
mGlobalWifiRunning = false;
mGlobalWifiRunningTimer.readSummaryFromParcelLocked(in);
+ for (int i=0; i<NUM_WIFI_STATES; i++) {
+ mWifiStateTimer[i].readSummaryFromParcelLocked(in);
+ }
mBluetoothOn = false;
mBluetoothOnTimer.readSummaryFromParcelLocked(in);
+ for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
+ mBluetoothStateTimer[i].readSummaryFromParcelLocked(in);
+ }
int NKW = in.readInt();
if (NKW > 10000) {
@@ -5574,7 +7020,22 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ int NWR = in.readInt();
+ if (NWR > 10000) {
+ Slog.w(TAG, "File corrupt: too many wakeup reasons " + NWR);
+ return;
+ }
+ for (int iwr = 0; iwr < NWR; iwr++) {
+ if (in.readInt() != 0) {
+ String reasonName = in.readString();
+ getWakeupReasonCounterLocked(reasonName).readSummaryFromParcelLocked(in);
+ }
+ }
+
sNumSpeedSteps = in.readInt();
+ if (sNumSpeedSteps < 0 || sNumSpeedSteps > 100) {
+ throw new BadParcelableException("Bad speed steps in data: " + sNumSpeedSteps);
+ }
final int NU = in.readInt();
if (NU > 10000) {
@@ -5634,12 +7095,15 @@ public final class BatteryStatsImpl extends BatteryStats {
}
if (in.readInt() != 0) {
- if (u.mNetworkActivityCounters == null) {
+ if (u.mNetworkByteActivityCounters == null) {
u.initNetworkActivityLocked();
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- u.mNetworkActivityCounters[i].readSummaryFromParcelLocked(in);
+ u.mNetworkByteActivityCounters[i].readSummaryFromParcelLocked(in);
+ u.mNetworkPacketActivityCounters[i].readSummaryFromParcelLocked(in);
}
+ u.mMobileRadioActiveTime.readSummaryFromParcelLocked(in);
+ u.mMobileRadioActiveCount.readSummaryFromParcelLocked(in);
}
int NW = in.readInt();
@@ -5693,7 +7157,7 @@ public final class BatteryStatsImpl extends BatteryStats {
p.mSpeedBins = new SamplingCounter[NSB];
for (int i=0; i<NSB; i++) {
if (in.readInt() != 0) {
- p.mSpeedBins[i] = new SamplingCounter(mUnpluggables);
+ p.mSpeedBins[i] = new SamplingCounter(mOnBatteryTimeBase);
p.mSpeedBins[i].readSummaryFromParcelLocked(in);
}
}
@@ -5734,50 +7198,66 @@ public final class BatteryStatsImpl extends BatteryStats {
* @param out the Parcel to be written to.
*/
public void writeSummaryToParcel(Parcel out) {
- // Need to update with current kernel wake lock counts.
- updateKernelWakelocksLocked();
- updateNetworkActivityLocked();
+ pullPendingStateUpdatesLocked();
final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
- final long NOW = getBatteryUptimeLocked(NOW_SYS);
- final long NOWREAL = getBatteryRealtimeLocked(NOWREAL_SYS);
out.writeInt(VERSION);
writeHistory(out, true);
out.writeInt(mStartCount);
- out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
- out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
+ out.writeLong(mStartClockTime);
+ mOnBatteryTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
+ mOnBatteryScreenOffTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
out.writeInt(mDischargeUnplugLevel);
+ out.writeInt(mDischargePlugLevel);
out.writeInt(mDischargeCurrentLevel);
+ out.writeInt(mCurrentBatteryLevel);
out.writeInt(getLowDischargeAmountSinceCharge());
out.writeInt(getHighDischargeAmountSinceCharge());
out.writeInt(getDischargeAmountScreenOnSinceCharge());
out.writeInt(getDischargeAmountScreenOffSinceCharge());
-
- mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ out.writeInt(mNumDischargeStepDurations);
+ out.writeLongArray(mDischargeStepDurations);
+ out.writeInt(mNumChargeStepDurations);
+ out.writeLongArray(mChargeStepDurations);
+
+ mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL);
+ mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
- mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL);
- mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mLowPowerModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
- mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL);
+ mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
- mPhoneSignalScanningTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ mPhoneSignalScanningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL);
+ mPhoneDataConnectionsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i].writeSummaryFromParcelLocked(out);
+ mNetworkByteActivityCounters[i].writeSummaryFromParcelLocked(out);
+ mNetworkPacketActivityCounters[i].writeSummaryFromParcelLocked(out);
+ }
+ mMobileRadioActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mMobileRadioActivePerAppTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out);
+ mMobileRadioActiveUnknownTime.writeSummaryFromParcelLocked(out);
+ mMobileRadioActiveUnknownCount.writeSummaryFromParcelLocked(out);
+ mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mGlobalWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ for (int i=0; i<NUM_WIFI_STATES; i++) {
+ mWifiStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ }
+ mBluetoothOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
+ mBluetoothStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
- mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
- mGlobalWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL);
- mBluetoothOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
out.writeInt(mKernelWakelockStats.size());
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
@@ -5785,7 +7265,19 @@ public final class BatteryStatsImpl extends BatteryStats {
if (kwlt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- ent.getValue().writeSummaryFromParcelLocked(out, NOWREAL);
+ kwlt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ out.writeInt(mWakeupReasonStats.size());
+ for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+ LongSamplingCounter counter = ent.getValue();
+ if (counter != null) {
+ out.writeInt(1);
+ out.writeString(ent.getKey());
+ counter.writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
}
@@ -5800,57 +7292,57 @@ public final class BatteryStatsImpl extends BatteryStats {
if (u.mWifiRunningTimer != null) {
out.writeInt(1);
- u.mWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ u.mWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
if (u.mFullWifiLockTimer != null) {
out.writeInt(1);
- u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
if (u.mWifiScanTimer != null) {
out.writeInt(1);
- u.mWifiScanTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ u.mWifiScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
for (int i = 0; i < Uid.NUM_WIFI_BATCHED_SCAN_BINS; i++) {
if (u.mWifiBatchedScanTimer[i] != null) {
out.writeInt(1);
- u.mWifiBatchedScanTimer[i].writeSummaryFromParcelLocked(out, NOWREAL);
+ u.mWifiBatchedScanTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
}
if (u.mWifiMulticastTimer != null) {
out.writeInt(1);
- u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
if (u.mAudioTurnedOnTimer != null) {
out.writeInt(1);
- u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
if (u.mVideoTurnedOnTimer != null) {
out.writeInt(1);
- u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
if (u.mForegroundActivityTimer != null) {
out.writeInt(1);
- u.mForegroundActivityTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ u.mForegroundActivityTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
if (u.mVibratorOnTimer != null) {
out.writeInt(1);
- u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
@@ -5864,13 +7356,16 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- if (u.mNetworkActivityCounters == null) {
+ if (u.mNetworkByteActivityCounters == null) {
out.writeInt(0);
} else {
out.writeInt(1);
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- u.mNetworkActivityCounters[i].writeSummaryFromParcelLocked(out);
+ u.mNetworkByteActivityCounters[i].writeSummaryFromParcelLocked(out);
+ u.mNetworkPacketActivityCounters[i].writeSummaryFromParcelLocked(out);
}
+ u.mMobileRadioActiveTime.writeSummaryFromParcelLocked(out);
+ u.mMobileRadioActiveCount.writeSummaryFromParcelLocked(out);
}
int NW = u.mWakelockStats.size();
@@ -5882,19 +7377,19 @@ public final class BatteryStatsImpl extends BatteryStats {
Uid.Wakelock wl = ent.getValue();
if (wl.mTimerFull != null) {
out.writeInt(1);
- wl.mTimerFull.writeSummaryFromParcelLocked(out, NOWREAL);
+ wl.mTimerFull.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
if (wl.mTimerPartial != null) {
out.writeInt(1);
- wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOWREAL);
+ wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
if (wl.mTimerWindow != null) {
out.writeInt(1);
- wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOWREAL);
+ wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
@@ -5910,7 +7405,7 @@ public final class BatteryStatsImpl extends BatteryStats {
Uid.Sensor se = ent.getValue();
if (se.mTimer != null) {
out.writeInt(1);
- se.mTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+ se.mTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
} else {
out.writeInt(0);
}
@@ -5957,7 +7452,8 @@ public final class BatteryStatsImpl extends BatteryStats {
: ps.mServiceStats.entrySet()) {
out.writeString(sent.getKey());
BatteryStatsImpl.Uid.Pkg.Serv ss = sent.getValue();
- long time = ss.getStartTimeToNowLocked(NOW);
+ long time = ss.getStartTimeToNowLocked(
+ mOnBatteryTimeBase.getUptime(NOW_SYS));
out.writeLong(time);
out.writeInt(ss.mStarts);
out.writeInt(ss.mLaunches);
@@ -5981,69 +7477,81 @@ public final class BatteryStatsImpl extends BatteryStats {
readHistory(in, false);
mStartCount = in.readInt();
- mBatteryUptime = in.readLong();
- mBatteryLastUptime = 0;
- mBatteryRealtime = in.readLong();
- mBatteryLastRealtime = 0;
+ mStartClockTime = in.readLong();
+ mUptime = in.readLong();
+ mUptimeStart = in.readLong();
+ mRealtime = in.readLong();
+ mRealtimeStart = in.readLong();
+ mOnBattery = in.readInt() != 0;
+ mOnBatteryInternal = false; // we are no longer really running.
+ mOnBatteryTimeBase.readFromParcel(in);
+ mOnBatteryScreenOffTimeBase.readFromParcel(in);
+
mScreenState = Display.STATE_UNKNOWN;
- mScreenOnTimer = new StopwatchTimer(null, -1, null, mUnpluggables, in);
+ mScreenOnTimer = new StopwatchTimer(null, -1, null, mOnBatteryTimeBase, in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i,
- null, mUnpluggables, in);
+ mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mOnBatteryTimeBase,
+ in);
}
+ mInteractive = false;
+ mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
mPhoneOn = false;
- mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
+ mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in);
+ mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i,
- null, mUnpluggables, in);
+ null, mOnBatteryTimeBase, in);
}
- mPhoneSignalScanningTimer = new StopwatchTimer(null, -200+1, null, mUnpluggables, in);
+ mPhoneSignalScanningTimer = new StopwatchTimer(null, -200+1, null, mOnBatteryTimeBase, in);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i,
- null, mUnpluggables, in);
+ null, mOnBatteryTimeBase, in);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables, in);
- }
+ mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ }
+ mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase, in);
+ mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase,
+ in);
+ mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
mWifiOn = false;
- mWifiOnTimer = new StopwatchTimer(null, -3, null, mUnpluggables, in);
+ mWifiOnTimer = new StopwatchTimer(null, -4, null, mOnBatteryTimeBase, in);
mGlobalWifiRunning = false;
- mGlobalWifiRunningTimer = new StopwatchTimer(null, -4, null, mUnpluggables, in);
+ mGlobalWifiRunningTimer = new StopwatchTimer(null, -5, null, mOnBatteryTimeBase, in);
+ for (int i=0; i<NUM_WIFI_STATES; i++) {
+ mWifiStateTimer[i] = new StopwatchTimer(null, -600-i,
+ null, mOnBatteryTimeBase, in);
+ }
mBluetoothOn = false;
- mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mUnpluggables, in);
+ mBluetoothOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase, in);
+ for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
+ mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i,
+ null, mOnBatteryTimeBase, in);
+ }
mAudioOn = false;
- mAudioOnTimer = new StopwatchTimer(null, -6, null, mUnpluggables);
+ mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
mVideoOn = false;
- mVideoOnTimer = new StopwatchTimer(null, -7, null, mUnpluggables);
- mInteractive = false;
- mInteractiveTimer = new StopwatchTimer(null, -8, null, mUnpluggables, in);
- mUptime = in.readLong();
- mUptimeStart = in.readLong();
- mLastUptime = 0;
- mRealtime = in.readLong();
- mRealtimeStart = in.readLong();
- mLastRealtime = 0;
- mOnBattery = in.readInt() != 0;
- mOnBatteryInternal = false; // we are no longer really running.
- mTrackBatteryPastUptime = in.readLong();
- mTrackBatteryUptimeStart = in.readLong();
- mTrackBatteryPastRealtime = in.readLong();
- mTrackBatteryRealtimeStart = in.readLong();
- mUnpluggedBatteryUptime = in.readLong();
- mUnpluggedBatteryRealtime = in.readLong();
+ mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
mDischargeUnplugLevel = in.readInt();
+ mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
+ mCurrentBatteryLevel = in.readInt();
mLowDischargeAmountSinceCharge = in.readInt();
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOn = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOff = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mNumDischargeStepDurations = in.readInt();
+ in.readLongArray(mDischargeStepDurations);
+ mNumChargeStepDurations = in.readInt();
+ in.readLongArray(mChargeStepDurations);
mLastWriteTime = in.readLong();
- mRadioDataUptime = in.readLong();
- mRadioDataStart = -1;
-
mBluetoothPingCount = in.readInt();
mBluetoothPingStart = -1;
@@ -6052,12 +7560,22 @@ public final class BatteryStatsImpl extends BatteryStats {
for (int ikw = 0; ikw < NKW; ikw++) {
if (in.readInt() != 0) {
String wakelockName = in.readString();
- in.readInt(); // Extra 0/1 written by Timer.writeTimerToParcel
- SamplingTimer kwlt = new SamplingTimer(mUnpluggables, mOnBattery, in);
+ SamplingTimer kwlt = new SamplingTimer(mOnBatteryTimeBase, in);
mKernelWakelockStats.put(wakelockName, kwlt);
}
}
+ mWakeupReasonStats.clear();
+ int NWR = in.readInt();
+ for (int iwr = 0; iwr < NWR; iwr++) {
+ if (in.readInt() != 0) {
+ String reasonName = in.readString();
+ LongSamplingCounter counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase,
+ in);
+ mWakeupReasonStats.put(reasonName, counter);
+ }
+ }
+
mPartialTimers.clear();
mFullTimers.clear();
mWindowTimers.clear();
@@ -6074,7 +7592,7 @@ public final class BatteryStatsImpl extends BatteryStats {
for (int i = 0; i < numUids; i++) {
int uid = in.readInt();
Uid u = new Uid(uid);
- u.readFromParcelLocked(mUnpluggables, in);
+ u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase, in);
mUidStats.append(uid, u);
}
}
@@ -6090,64 +7608,75 @@ public final class BatteryStatsImpl extends BatteryStats {
@SuppressWarnings("unused")
void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
// Need to update with current kernel wake lock counts.
- updateKernelWakelocksLocked();
- updateNetworkActivityLocked();
+ pullPendingStateUpdatesLocked();
final long uSecUptime = SystemClock.uptimeMillis() * 1000;
final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
- final long batteryUptime = getBatteryUptimeLocked(uSecUptime);
- final long batteryRealtime = getBatteryRealtimeLocked(uSecRealtime);
+ final long batteryRealtime = mOnBatteryTimeBase.getRealtime(uSecRealtime);
+ final long batteryScreenOffRealtime = mOnBatteryScreenOffTimeBase.getRealtime(uSecRealtime);
out.writeInt(MAGIC);
writeHistory(out, false);
out.writeInt(mStartCount);
- out.writeLong(mBatteryUptime);
- out.writeLong(mBatteryRealtime);
- mScreenOnTimer.writeToParcel(out, batteryRealtime);
+ out.writeLong(mStartClockTime);
+ out.writeLong(mUptime);
+ out.writeLong(mUptimeStart);
+ out.writeLong(mRealtime);
+ out.writeLong(mRealtimeStart);
+ out.writeInt(mOnBattery ? 1 : 0);
+ mOnBatteryTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
+ mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
+
+ mScreenOnTimer.writeToParcel(out, uSecRealtime);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i].writeToParcel(out, batteryRealtime);
+ mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime);
}
- mInteractiveTimer.writeToParcel(out, batteryRealtime);
- mPhoneOnTimer.writeToParcel(out, batteryRealtime);
+ mInteractiveTimer.writeToParcel(out, uSecRealtime);
+ mLowPowerModeEnabledTimer.writeToParcel(out, uSecRealtime);
+ mPhoneOnTimer.writeToParcel(out, uSecRealtime);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
- mPhoneSignalStrengthsTimer[i].writeToParcel(out, batteryRealtime);
+ mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
}
- mPhoneSignalScanningTimer.writeToParcel(out, batteryRealtime);
+ mPhoneSignalScanningTimer.writeToParcel(out, uSecRealtime);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i].writeToParcel(out, batteryRealtime);
+ mPhoneDataConnectionsTimer[i].writeToParcel(out, uSecRealtime);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkActivityCounters[i].writeToParcel(out);
+ mNetworkByteActivityCounters[i].writeToParcel(out);
+ mNetworkPacketActivityCounters[i].writeToParcel(out);
+ }
+ mMobileRadioActiveTimer.writeToParcel(out, uSecRealtime);
+ mMobileRadioActivePerAppTimer.writeToParcel(out, uSecRealtime);
+ mMobileRadioActiveAdjustedTime.writeToParcel(out);
+ mMobileRadioActiveUnknownTime.writeToParcel(out);
+ mMobileRadioActiveUnknownCount.writeToParcel(out);
+ mWifiOnTimer.writeToParcel(out, uSecRealtime);
+ mGlobalWifiRunningTimer.writeToParcel(out, uSecRealtime);
+ for (int i=0; i<NUM_WIFI_STATES; i++) {
+ mWifiStateTimer[i].writeToParcel(out, uSecRealtime);
+ }
+ mBluetoothOnTimer.writeToParcel(out, uSecRealtime);
+ for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
+ mBluetoothStateTimer[i].writeToParcel(out, uSecRealtime);
}
- mWifiOnTimer.writeToParcel(out, batteryRealtime);
- mGlobalWifiRunningTimer.writeToParcel(out, batteryRealtime);
- mBluetoothOnTimer.writeToParcel(out, batteryRealtime);
- out.writeLong(mUptime);
- out.writeLong(mUptimeStart);
- out.writeLong(mRealtime);
- out.writeLong(mRealtimeStart);
- out.writeInt(mOnBattery ? 1 : 0);
- out.writeLong(batteryUptime);
- out.writeLong(mTrackBatteryUptimeStart);
- out.writeLong(batteryRealtime);
- out.writeLong(mTrackBatteryRealtimeStart);
- out.writeLong(mUnpluggedBatteryUptime);
- out.writeLong(mUnpluggedBatteryRealtime);
out.writeInt(mDischargeUnplugLevel);
+ out.writeInt(mDischargePlugLevel);
out.writeInt(mDischargeCurrentLevel);
+ out.writeInt(mCurrentBatteryLevel);
out.writeInt(mLowDischargeAmountSinceCharge);
out.writeInt(mHighDischargeAmountSinceCharge);
out.writeInt(mDischargeAmountScreenOn);
out.writeInt(mDischargeAmountScreenOnSinceCharge);
out.writeInt(mDischargeAmountScreenOff);
out.writeInt(mDischargeAmountScreenOffSinceCharge);
+ out.writeInt(mNumDischargeStepDurations);
+ out.writeLongArray(mDischargeStepDurations);
+ out.writeInt(mNumChargeStepDurations);
+ out.writeLongArray(mChargeStepDurations);
out.writeLong(mLastWriteTime);
- // Write radio uptime for data
- out.writeLong(getRadioDataUptime());
-
out.writeInt(getBluetoothPingCount());
if (inclUids) {
@@ -6157,7 +7686,18 @@ public final class BatteryStatsImpl extends BatteryStats {
if (kwlt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- Timer.writeTimerToParcel(out, kwlt, batteryRealtime);
+ kwlt.writeToParcel(out, uSecRealtime);
+ } else {
+ out.writeInt(0);
+ }
+ }
+ out.writeInt(mWakeupReasonStats.size());
+ for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+ LongSamplingCounter counter = ent.getValue();
+ if (counter != null) {
+ out.writeInt(1);
+ out.writeString(ent.getKey());
+ counter.writeToParcel(out);
} else {
out.writeInt(0);
}
@@ -6175,7 +7715,7 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(mUidStats.keyAt(i));
Uid uid = mUidStats.valueAt(i);
- uid.writeToParcelLocked(out, batteryRealtime);
+ uid.writeToParcelLocked(out, uSecRealtime);
}
} else {
out.writeInt(0);
@@ -6195,12 +7735,15 @@ public final class BatteryStatsImpl extends BatteryStats {
public void prepareForDumpLocked() {
// Need to retrieve current kernel wake lock stats before printing.
- updateKernelWakelocksLocked();
- updateNetworkActivityLocked();
+ pullPendingStateUpdatesLocked();
}
- public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly, int reqUid) {
+ public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
if (DEBUG) {
+ pw.println("mOnBatteryTimeBase:");
+ mOnBatteryTimeBase.dump(pw, " ");
+ pw.println("mOnBatteryScreenOffTimeBase:");
+ mOnBatteryScreenOffTimeBase.dump(pw, " ");
Printer pr = new PrintWriterPrinter(pw);
pr.println("*** Screen timer:");
mScreenOnTimer.logState(pr, " ");
@@ -6210,6 +7753,8 @@ public final class BatteryStatsImpl extends BatteryStats {
}
pr.println("*** Interactive timer:");
mInteractiveTimer.logState(pr, " ");
+ pr.println("*** Low power mode timer:");
+ mLowPowerModeEnabledTimer.logState(pr, " ");
pr.println("*** Phone timer:");
mPhoneOnTimer.logState(pr, " ");
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -6222,13 +7767,26 @@ public final class BatteryStatsImpl extends BatteryStats {
pr.println("*** Data connection type #" + i + ":");
mPhoneDataConnectionsTimer[i].logState(pr, " ");
}
+ pr.println("*** mMobileRadioPowerState=" + mMobileRadioPowerState);
+ pr.println("*** Mobile network active timer:");
+ mMobileRadioActiveTimer.logState(pr, " ");
+ pr.println("*** Mobile network active adjusted timer:");
+ mMobileRadioActiveAdjustedTime.logState(pr, " ");
pr.println("*** Wifi timer:");
mWifiOnTimer.logState(pr, " ");
pr.println("*** WifiRunning timer:");
mGlobalWifiRunningTimer.logState(pr, " ");
+ for (int i=0; i<NUM_WIFI_STATES; i++) {
+ pr.println("*** Wifi state #" + i + ":");
+ mWifiStateTimer[i].logState(pr, " ");
+ }
pr.println("*** Bluetooth timer:");
mBluetoothOnTimer.logState(pr, " ");
+ for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
+ pr.println("*** Bluetooth active type #" + i + ":");
+ mBluetoothStateTimer[i].logState(pr, " ");
+ }
}
- super.dumpLocked(pw, isUnpluggedOnly, reqUid);
+ super.dumpLocked(context, pw, flags, reqUid, histStart);
}
}
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index d9e3ef6..17685fd 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -22,9 +22,6 @@ import android.os.Looper;
import android.os.Message;
public class HandlerCaller {
-
- public final Context mContext;
-
final Looper mMainLooper;
final Handler mH;
@@ -47,7 +44,6 @@ public class HandlerCaller {
public HandlerCaller(Context context, Looper looper, Callback callback,
boolean asyncHandler) {
- mContext = context;
mMainLooper = looper != null ? looper : context.getMainLooper();
mH = new MyHandler(mMainLooper, asyncHandler);
mCallback = callback;
@@ -85,7 +81,27 @@ public class HandlerCaller {
public void sendMessage(Message msg) {
mH.sendMessage(msg);
}
-
+
+ public SomeArgs sendMessageAndWait(Message msg) {
+ if (Looper.myLooper() == mH.getLooper()) {
+ throw new IllegalStateException("Can't wait on same thread as looper");
+ }
+ SomeArgs args = (SomeArgs)msg.obj;
+ args.mWaitState = SomeArgs.WAIT_WAITING;
+ mH.sendMessage(msg);
+ synchronized (args) {
+ while (args.mWaitState == SomeArgs.WAIT_WAITING) {
+ try {
+ args.wait();
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+ }
+ args.mWaitState = SomeArgs.WAIT_NONE;
+ return args;
+ }
+
public Message obtainMessage(int what) {
return mH.obtainMessage(what);
}
@@ -136,6 +152,14 @@ public class HandlerCaller {
return mH.obtainMessage(what, arg1, 0, args);
}
+ public Message obtainMessageIOOO(int what, int arg1, Object arg2, Object arg3, Object arg4) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = arg2;
+ args.arg2 = arg3;
+ args.arg3 = arg4;
+ return mH.obtainMessage(what, arg1, 0, args);
+ }
+
public Message obtainMessageOO(int what, Object arg1, Object arg2) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = arg1;
@@ -161,6 +185,17 @@ public class HandlerCaller {
return mH.obtainMessage(what, 0, 0, args);
}
+ public Message obtainMessageOOOOO(int what, Object arg1, Object arg2,
+ Object arg3, Object arg4, Object arg5) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = arg1;
+ args.arg2 = arg2;
+ args.arg3 = arg3;
+ args.arg4 = arg4;
+ args.arg5 = arg5;
+ return mH.obtainMessage(what, 0, 0, args);
+ }
+
public Message obtainMessageIIII(int what, int arg1, int arg2,
int arg3, int arg4) {
SomeArgs args = SomeArgs.obtain();
diff --git a/core/java/com/android/internal/os/PkgUsageStats.aidl b/core/java/com/android/internal/os/PkgUsageStats.aidl
deleted file mode 100644
index 8305271..0000000
--- a/core/java/com/android/internal/os/PkgUsageStats.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/* //device/java/android/android/content/Intent.aidl
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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.os;
-
-parcelable PkgUsageStats;
diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java
deleted file mode 100644
index 8c2c405..0000000
--- a/core/java/com/android/internal/os/PkgUsageStats.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.os;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * implementation of PkgUsageStats associated with an
- * application package.
- * @hide
- */
-public class PkgUsageStats implements Parcelable {
- public String packageName;
- public int launchCount;
- public long usageTime;
- public Map<String, Long> componentResumeTimes;
-
- public static final Parcelable.Creator<PkgUsageStats> CREATOR
- = new Parcelable.Creator<PkgUsageStats>() {
- public PkgUsageStats createFromParcel(Parcel in) {
- return new PkgUsageStats(in);
- }
-
- public PkgUsageStats[] newArray(int size) {
- return new PkgUsageStats[size];
- }
- };
-
- public String toString() {
- return "PkgUsageStats{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + packageName + "}";
- }
-
- public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) {
- packageName = pkgName;
- launchCount = count;
- usageTime = time;
- componentResumeTimes = new HashMap<String, Long>(lastResumeTimes);
- }
-
- public PkgUsageStats(Parcel source) {
- packageName = source.readString();
- launchCount = source.readInt();
- usageTime = source.readLong();
- final int N = source.readInt();
- componentResumeTimes = new HashMap<String, Long>(N);
- for (int i = 0; i < N; i++) {
- String component = source.readString();
- long lastResumeTime = source.readLong();
- componentResumeTimes.put(component, lastResumeTime);
- }
- }
-
- public PkgUsageStats(PkgUsageStats pStats) {
- packageName = pStats.packageName;
- launchCount = pStats.launchCount;
- usageTime = pStats.usageTime;
- componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel dest, int parcelableFlags) {
- dest.writeString(packageName);
- dest.writeInt(launchCount);
- dest.writeLong(usageTime);
- dest.writeInt(componentResumeTimes.size());
- for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) {
- dest.writeString(ent.getKey());
- dest.writeLong(ent.getValue());
- }
- }
-}
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index 6fb72f1..7edf4cc 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -35,6 +35,11 @@ public final class SomeArgs {
private boolean mInPool;
+ static final int WAIT_NONE = 0;
+ static final int WAIT_WAITING = 1;
+ static final int WAIT_FINISHED = 2;
+ int mWaitState = WAIT_NONE;
+
public Object arg1;
public Object arg2;
public Object arg3;
@@ -70,6 +75,9 @@ public final class SomeArgs {
if (mInPool) {
throw new IllegalStateException("Already recycled.");
}
+ if (mWaitState != WAIT_NONE) {
+ return;
+ }
synchronized (sPoolLock) {
clear();
if (sPoolSize < MAX_POOL_SIZE) {
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 63ff5a0..a5421f5 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -25,12 +25,24 @@ import android.os.Bundle;
interface IKeyguardService {
boolean isShowing();
boolean isSecure();
- boolean isShowingAndNotHidden();
+ boolean isShowingAndNotOccluded();
boolean isInputRestricted();
boolean isDismissable();
oneway void verifyUnlock(IKeyguardExitCallback callback);
oneway void keyguardDone(boolean authenticated, boolean wakeup);
- oneway void setHidden(boolean isHidden);
+
+ /**
+ * Sets the Keyguard as occluded when a window dismisses the Keyguard with flag
+ * FLAG_SHOW_ON_LOCK_SCREEN.
+ *
+ * @param isOccluded Whether the Keyguard is occluded by another window.
+ * @return See IKeyguardServiceConstants.KEYGUARD_SERVICE_SET_OCCLUDED_*. This is needed because
+ * PhoneWindowManager needs to set these flags immediately and can't wait for the
+ * Keyguard thread to pick it up. In the hidden case, PhoneWindowManager is solely
+ * responsible to make sure that the flags are unset.
+ */
+ int setOccluded(boolean isOccluded);
+
oneway void dismiss();
oneway void onDreamingStarted();
oneway void onDreamingStopped();
@@ -44,4 +56,13 @@ interface IKeyguardService {
oneway void dispatch(in MotionEvent event);
oneway void launchCamera();
oneway void onBootCompleted();
+
+ /**
+ * Notifies that the activity behind has now been drawn and it's safe to remove the wallpaper
+ * and keyguard flag.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds
+ */
+ oneway void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
}
diff --git a/core/java/com/android/internal/policy/IKeyguardServiceConstants.java b/core/java/com/android/internal/policy/IKeyguardServiceConstants.java
new file mode 100644
index 0000000..b88174a
--- /dev/null
+++ b/core/java/com/android/internal/policy/IKeyguardServiceConstants.java
@@ -0,0 +1,41 @@
+/*
+ * 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.internal.policy;
+
+/**
+ * @hide
+ */
+public class IKeyguardServiceConstants {
+
+ /**
+ * Constant for {@link com.android.internal.policy.IKeyguardService#setHidden(boolean)}:
+ * Don't change the keyguard window flags.
+ */
+ public static final int KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_NONE = 0;
+
+ /**
+ * Constant for {@link com.android.internal.policy.IKeyguardService#setHidden(boolean)}:
+ * Set the keyguard window flags to FLAG_SHOW_WALLPAPER and PRIVATE_FLAG_KEYGUARD.
+ */
+ public static final int KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_SET_FLAGS = 1;
+
+ /**
+ * Constant for {@link com.android.internal.policy.IKeyguardService#setHidden(boolean)}:
+ * Unset the keyguard window flags to FLAG_SHOW_WALLPAPER and PRIVATE_FLAG_KEYGUARD.
+ */
+ public static final int KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_UNSET_FLAGS = 2;
+}
diff --git a/core/java/com/android/internal/preference/YesNoPreference.java b/core/java/com/android/internal/preference/YesNoPreference.java
index cf68a58..7abf416 100644
--- a/core/java/com/android/internal/preference/YesNoPreference.java
+++ b/core/java/com/android/internal/preference/YesNoPreference.java
@@ -31,15 +31,19 @@ import android.util.AttributeSet;
*/
public class YesNoPreference extends DialogPreference {
private boolean mWasPositiveResult;
-
- public YesNoPreference(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+
+ public YesNoPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public YesNoPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
}
public YesNoPreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.yesNoPreferenceStyle);
}
-
+
public YesNoPreference(Context context) {
this(context, null);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index d1d1a52..a01e9b7 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -24,20 +24,24 @@ oneway interface IStatusBar
{
void setIcon(int index, in StatusBarIcon icon);
void removeIcon(int index);
- void addNotification(IBinder key, in StatusBarNotification notification);
- void updateNotification(IBinder key, in StatusBarNotification notification);
- void removeNotification(IBinder key);
+ void addNotification(in StatusBarNotification notification);
+ void updateNotification(in StatusBarNotification notification);
+ void removeNotification(String key);
void disable(int state);
void animateExpandNotificationsPanel();
void animateExpandSettingsPanel();
void animateCollapsePanels();
void setSystemUiVisibility(int vis, int mask);
void topAppWindowChanged(boolean menuVisible);
- void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
+ void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher);
void setHardKeyboardStatus(boolean available, boolean enabled);
+ void setWindowState(int window, int state);
+
+ void showRecentApps(boolean triggeredFromAltTab);
+ void hideRecentApps(boolean triggeredFromAltTab);
void toggleRecentApps();
void preloadRecentApps();
void cancelPreloadRecentApps();
- void setWindowState(int window, int state);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 97ea7d8..a3b417f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -31,25 +31,32 @@ interface IStatusBarService
void setIconVisibility(String slot, boolean visible);
void removeIcon(String slot);
void topAppWindowChanged(boolean menuVisible);
- void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
+ void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher);
void expandSettingsPanel();
void setCurrentUser(int newUserId);
// ---- Methods below are for use by the status bar policy services ----
// You need the STATUS_BAR_SERVICE permission
void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
- out List<IBinder> notificationKeys, out List<StatusBarNotification> notifications,
- out int[] switches, out List<IBinder> binders);
+ out List<StatusBarNotification> notifications, out int[] switches,
+ out List<IBinder> binders);
void onPanelRevealed();
- void onNotificationClick(String pkg, String tag, int id);
+ void onPanelHidden();
+ void onNotificationClick(String key);
void onNotificationError(String pkg, String tag, int id,
- int uid, int initialPid, String message);
- void onClearAllNotifications();
- void onNotificationClear(String pkg, String tag, int id);
+ int uid, int initialPid, String message, int userId);
+ void onClearAllNotifications(int userId);
+ void onNotificationClear(String pkg, String tag, int id, int userId);
+ void onNotificationVisibilityChanged(
+ in String[] newlyVisibleKeys, in String[] noLongerVisibleKeys);
void setSystemUiVisibility(int vis, int mask);
void setHardKeyboardEnabled(boolean enabled);
+ void setWindowState(int window, int state);
+
+ void showRecentApps(boolean triggeredFromAltTab);
+ void hideRecentApps(boolean triggeredFromAltTab);
void toggleRecentApps();
void preloadRecentApps();
void cancelPreloadRecentApps();
- void setWindowState(int window, int state);
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 9137d3c..d66ef83 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -16,11 +16,10 @@
package com.android.internal.util;
-import java.lang.reflect.Array;
+import dalvik.system.VMRuntime;
+import libcore.util.EmptyArray;
-// XXX these should be changed to reflect the actual memory allocator we use.
-// it looks like right now objects want to be powers of 2 minus 8
-// and the array size eats another 4 bytes
+import java.lang.reflect.Array;
/**
* ArrayUtils contains some methods that you can call to find out
@@ -28,46 +27,42 @@ import java.lang.reflect.Array;
*/
public class ArrayUtils
{
- private static Object[] EMPTY = new Object[0];
private static final int CACHE_SIZE = 73;
private static Object[] sCache = new Object[CACHE_SIZE];
private ArrayUtils() { /* cannot be instantiated */ }
- public static int idealByteArraySize(int need) {
- for (int i = 4; i < 32; i++)
- if (need <= (1 << i) - 12)
- return (1 << i) - 12;
-
- return need;
+ public static byte[] newUnpaddedByteArray(int minLen) {
+ return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen);
}
- public static int idealBooleanArraySize(int need) {
- return idealByteArraySize(need);
+ public static char[] newUnpaddedCharArray(int minLen) {
+ return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen);
}
- public static int idealShortArraySize(int need) {
- return idealByteArraySize(need * 2) / 2;
+ public static int[] newUnpaddedIntArray(int minLen) {
+ return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen);
}
- public static int idealCharArraySize(int need) {
- return idealByteArraySize(need * 2) / 2;
+ public static boolean[] newUnpaddedBooleanArray(int minLen) {
+ return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen);
}
- public static int idealIntArraySize(int need) {
- return idealByteArraySize(need * 4) / 4;
+ public static long[] newUnpaddedLongArray(int minLen) {
+ return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen);
}
- public static int idealFloatArraySize(int need) {
- return idealByteArraySize(need * 4) / 4;
+ public static float[] newUnpaddedFloatArray(int minLen) {
+ return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen);
}
- public static int idealObjectArraySize(int need) {
- return idealByteArraySize(need * 4) / 4;
+ public static Object[] newUnpaddedObjectArray(int minLen) {
+ return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen);
}
- public static int idealLongArraySize(int need) {
- return idealByteArraySize(need * 8) / 8;
+ @SuppressWarnings("unchecked")
+ public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
+ return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen);
}
/**
@@ -102,9 +97,10 @@ public class ArrayUtils
* it will return the same empty array every time to avoid reallocation,
* although this is not guaranteed.
*/
+ @SuppressWarnings("unchecked")
public static <T> T[] emptyArray(Class<T> kind) {
if (kind == Object.class) {
- return (T[]) EMPTY;
+ return (T[]) EmptyArray.OBJECT;
}
int bucket = ((System.identityHashCode(kind) / 8) & 0x7FFFFFFF) % CACHE_SIZE;
@@ -121,6 +117,13 @@ public class ArrayUtils
}
/**
+ * Checks if given array is null or has zero elements.
+ */
+ public static <T> boolean isEmpty(T[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
* Checks that value is present as at least one of the elements of the array.
* @param array the array to check in
* @param value the value to check for
@@ -166,6 +169,15 @@ public class ArrayUtils
return false;
}
+ public static boolean contains(long[] array, long value) {
+ for (long element : array) {
+ if (element == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static long total(long[] array) {
long total = 0;
for (long value : array) {
@@ -226,6 +238,14 @@ public class ArrayUtils
return array;
}
+ /**
+ * Appends a new value to a copy of the array and returns the copy. If
+ * the value is already present, the original array is returned
+ * @param cur The original array, or null to represent an empty array.
+ * @param val The value to add.
+ * @return A new array that contains all of the values of the original array
+ * with the new value added, or the original array.
+ */
public static int[] appendInt(int[] cur, int val) {
if (cur == null) {
return new int[] { val };
@@ -261,4 +281,48 @@ public class ArrayUtils
}
return cur;
}
+
+ /**
+ * Appends a new value to a copy of the array and returns the copy. If
+ * the value is already present, the original array is returned
+ * @param cur The original array, or null to represent an empty array.
+ * @param val The value to add.
+ * @return A new array that contains all of the values of the original array
+ * with the new value added, or the original array.
+ */
+ public static long[] appendLong(long[] cur, long val) {
+ if (cur == null) {
+ return new long[] { val };
+ }
+ final int N = cur.length;
+ for (int i = 0; i < N; i++) {
+ if (cur[i] == val) {
+ return cur;
+ }
+ }
+ long[] ret = new long[N + 1];
+ System.arraycopy(cur, 0, ret, 0, N);
+ ret[N] = val;
+ return ret;
+ }
+
+ public static long[] removeLong(long[] cur, long val) {
+ if (cur == null) {
+ return null;
+ }
+ final int N = cur.length;
+ for (int i = 0; i < N; i++) {
+ if (cur[i] == val) {
+ long[] ret = new long[N - 1];
+ if (i > 0) {
+ System.arraycopy(cur, 0, ret, 0, i);
+ }
+ if (i < (N - 1)) {
+ System.arraycopy(cur, i + 1, ret, i, N - i - 1);
+ }
+ return ret;
+ }
+ }
+ return cur;
+ }
}
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 52281d9..34f62ba 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -450,6 +450,7 @@ public class AsyncChannel {
public void disconnect() {
if ((mConnection != null) && (mSrcContext != null)) {
mSrcContext.unbindService(mConnection);
+ mConnection = null;
}
try {
// Send the DISCONNECTED, although it may not be received
@@ -463,10 +464,12 @@ public class AsyncChannel {
// Tell source we're disconnected.
if (mSrcHandler != null) {
replyDisconnected(STATUS_SUCCESSFUL);
+ mSrcHandler = null;
}
// Unlink only when bindService isn't used
if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
+ mDeathMonitor = null;
}
}
diff --git a/core/java/com/android/internal/util/GrowingArrayUtils.java b/core/java/com/android/internal/util/GrowingArrayUtils.java
new file mode 100644
index 0000000..b4d2d730
--- /dev/null
+++ b/core/java/com/android/internal/util/GrowingArrayUtils.java
@@ -0,0 +1,196 @@
+/*
+ * 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.internal.util;
+
+/**
+ * A helper class that aims to provide comparable growth performance to ArrayList, but on primitive
+ * arrays. Common array operations are implemented for efficient use in dynamic containers.
+ *
+ * All methods in this class assume that the length of an array is equivalent to its capacity and
+ * NOT the number of elements in the array. The current size of the array is always passed in as a
+ * parameter.
+ *
+ * @hide
+ */
+public final class GrowingArrayUtils {
+
+ /**
+ * Appends an element to the end of the array, growing the array if there is no more room.
+ * @param array The array to which to append the element. This must NOT be null.
+ * @param currentSize The number of elements in the array. Must be less than or equal to
+ * array.length.
+ * @param element The element to append.
+ * @return the array to which the element was appended. This may be different than the given
+ * array.
+ */
+ public static <T> T[] append(T[] array, int currentSize, T element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 > array.length) {
+ @SuppressWarnings("unchecked")
+ T[] newArray = ArrayUtils.newUnpaddedArray(
+ (Class<T>) array.getClass().getComponentType(), growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, currentSize);
+ array = newArray;
+ }
+ array[currentSize] = element;
+ return array;
+ }
+
+ /**
+ * Primitive int version of {@link #append(Object[], int, Object)}.
+ */
+ public static int[] append(int[] array, int currentSize, int element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 > array.length) {
+ int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, currentSize);
+ array = newArray;
+ }
+ array[currentSize] = element;
+ return array;
+ }
+
+ /**
+ * Primitive long version of {@link #append(Object[], int, Object)}.
+ */
+ public static long[] append(long[] array, int currentSize, long element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 > array.length) {
+ long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, currentSize);
+ array = newArray;
+ }
+ array[currentSize] = element;
+ return array;
+ }
+
+ /**
+ * Primitive boolean version of {@link #append(Object[], int, Object)}.
+ */
+ public static boolean[] append(boolean[] array, int currentSize, boolean element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 > array.length) {
+ boolean[] newArray = ArrayUtils.newUnpaddedBooleanArray(growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, currentSize);
+ array = newArray;
+ }
+ array[currentSize] = element;
+ return array;
+ }
+
+ /**
+ * Inserts an element into the array at the specified index, growing the array if there is no
+ * more room.
+ *
+ * @param array The array to which to append the element. Must NOT be null.
+ * @param currentSize The number of elements in the array. Must be less than or equal to
+ * array.length.
+ * @param element The element to insert.
+ * @return the array to which the element was appended. This may be different than the given
+ * array.
+ */
+ public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 <= array.length) {
+ System.arraycopy(array, index, array, index + 1, currentSize - index);
+ array[index] = element;
+ return array;
+ }
+
+ @SuppressWarnings("unchecked")
+ T[] newArray = ArrayUtils.newUnpaddedArray((Class<T>)array.getClass().getComponentType(),
+ growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, index);
+ newArray[index] = element;
+ System.arraycopy(array, index, newArray, index + 1, array.length - index);
+ return newArray;
+ }
+
+ /**
+ * Primitive int version of {@link #insert(Object[], int, int, Object)}.
+ */
+ public static int[] insert(int[] array, int currentSize, int index, int element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 <= array.length) {
+ System.arraycopy(array, index, array, index + 1, currentSize - index);
+ array[index] = element;
+ return array;
+ }
+
+ int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, index);
+ newArray[index] = element;
+ System.arraycopy(array, index, newArray, index + 1, array.length - index);
+ return newArray;
+ }
+
+ /**
+ * Primitive long version of {@link #insert(Object[], int, int, Object)}.
+ */
+ public static long[] insert(long[] array, int currentSize, int index, long element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 <= array.length) {
+ System.arraycopy(array, index, array, index + 1, currentSize - index);
+ array[index] = element;
+ return array;
+ }
+
+ long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, index);
+ newArray[index] = element;
+ System.arraycopy(array, index, newArray, index + 1, array.length - index);
+ return newArray;
+ }
+
+ /**
+ * Primitive boolean version of {@link #insert(Object[], int, int, Object)}.
+ */
+ public static boolean[] insert(boolean[] array, int currentSize, int index, boolean element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 <= array.length) {
+ System.arraycopy(array, index, array, index + 1, currentSize - index);
+ array[index] = element;
+ return array;
+ }
+
+ boolean[] newArray = ArrayUtils.newUnpaddedBooleanArray(growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, index);
+ newArray[index] = element;
+ System.arraycopy(array, index, newArray, index + 1, array.length - index);
+ return newArray;
+ }
+
+ /**
+ * Given the current size of an array, returns an ideal size to which the array should grow.
+ * This is typically double the given size, but should not be relied upon to do so in the
+ * future.
+ */
+ public static int growSize(int currentSize) {
+ return currentSize <= 4 ? 8 : currentSize * 2;
+ }
+
+ // Uninstantiable
+ private GrowingArrayUtils() {}
+}
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
new file mode 100644
index 0000000..a5ce6e0
--- /dev/null
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -0,0 +1,84 @@
+/*
+ * 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.internal.util;
+
+import android.graphics.Bitmap;
+
+/**
+ * Utility class for image analysis and processing.
+ *
+ * @hide
+ */
+public class ImageUtils {
+
+ // Amount (max is 255) that two channels can differ before the color is no longer "gray".
+ private static final int TOLERANCE = 20;
+
+ // Alpha amount for which values below are considered transparent.
+ private static final int ALPHA_TOLERANCE = 50;
+
+ private int[] mTempBuffer;
+
+ /**
+ * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
+ * gray".
+ */
+ public boolean isGrayscale(Bitmap bitmap) {
+ final int height = bitmap.getHeight();
+ final int width = bitmap.getWidth();
+ int size = height*width;
+
+ ensureBufferSize(size);
+ bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height);
+ for (int i = 0; i < size; i++) {
+ if (!isGrayscale(mTempBuffer[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Makes sure that {@code mTempBuffer} has at least length {@code size}.
+ */
+ private void ensureBufferSize(int size) {
+ if (mTempBuffer == null || mTempBuffer.length < size) {
+ mTempBuffer = new int[size];
+ }
+ }
+
+ /**
+ * Classifies a color as grayscale or not. Grayscale here means "very close to a perfect
+ * gray"; if all three channels are approximately equal, this will return true.
+ *
+ * Note that really transparent colors are always grayscale.
+ */
+ public static boolean isGrayscale(int color) {
+ int alpha = 0xFF & (color >> 24);
+ if (alpha < ALPHA_TOLERANCE) {
+ return true;
+ }
+
+ int r = 0xFF & (color >> 16);
+ int g = 0xFF & (color >> 8);
+ int b = 0xFF & color;
+
+ return Math.abs(r - g) < TOLERANCE
+ && Math.abs(r - b) < TOLERANCE
+ && Math.abs(g - b) < TOLERANCE;
+ }
+}
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
new file mode 100644
index 0000000..f38cbde
--- /dev/null
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -0,0 +1,197 @@
+/*
+ * 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.internal.util;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.TextAppearanceSpan;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.Arrays;
+import java.util.WeakHashMap;
+
+/**
+ * Helper class to process legacy (Holo) notifications to make them look like quantum notifications.
+ *
+ * @hide
+ */
+public class NotificationColorUtil {
+
+ private static final String TAG = "NotificationColorUtil";
+
+ private static final Object sLock = new Object();
+ private static NotificationColorUtil sInstance;
+
+ private final ImageUtils mImageUtils = new ImageUtils();
+ private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache =
+ new WeakHashMap<Bitmap, Pair<Boolean, Integer>>();
+
+ public static NotificationColorUtil getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new NotificationColorUtil();
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
+ * gray".
+ *
+ * @param bitmap The bitmap to test.
+ * @return Whether the bitmap is grayscale.
+ */
+ public boolean isGrayscale(Bitmap bitmap) {
+ synchronized (sLock) {
+ Pair<Boolean, Integer> cached = mGrayscaleBitmapCache.get(bitmap);
+ if (cached != null) {
+ if (cached.second == bitmap.getGenerationId()) {
+ return cached.first;
+ }
+ }
+ }
+ boolean result;
+ int generationId;
+ synchronized (mImageUtils) {
+ result = mImageUtils.isGrayscale(bitmap);
+
+ // generationId and the check whether the Bitmap is grayscale can't be read atomically
+ // here. However, since the thread is in the process of posting the notification, we can
+ // assume that it doesn't modify the bitmap while we are checking the pixels.
+ generationId = bitmap.getGenerationId();
+ }
+ synchronized (sLock) {
+ mGrayscaleBitmapCache.put(bitmap, Pair.create(result, generationId));
+ }
+ return result;
+ }
+
+ /**
+ * Checks whether a drawable is grayscale. Grayscale here means "very close to a perfect
+ * gray".
+ *
+ * @param d The drawable to test.
+ * @return Whether the drawable is grayscale.
+ */
+ public boolean isGrayscale(Drawable d) {
+ if (d == null) {
+ return false;
+ } else if (d instanceof BitmapDrawable) {
+ BitmapDrawable bd = (BitmapDrawable) d;
+ return bd.getBitmap() != null && isGrayscale(bd.getBitmap());
+ } else if (d instanceof AnimationDrawable) {
+ AnimationDrawable ad = (AnimationDrawable) d;
+ int count = ad.getNumberOfFrames();
+ return count > 0 && isGrayscale(ad.getFrame(0));
+ } else if (d instanceof VectorDrawable) {
+ // We just assume you're doing the right thing if using vectors
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks whether a drawable with a resoure id is grayscale. Grayscale here means "very close
+ * to a perfect gray".
+ *
+ * @param context The context to load the drawable from.
+ * @return Whether the drawable is grayscale.
+ */
+ public boolean isGrayscale(Context context, int drawableResId) {
+ if (drawableResId != 0) {
+ try {
+ return isGrayscale(context.getDrawable(drawableResId));
+ } catch (Resources.NotFoundException ex) {
+ Log.e(TAG, "Drawable not found: " + drawableResId);
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Inverts all the grayscale colors set by {@link android.text.style.TextAppearanceSpan}s on
+ * the text.
+ *
+ * @param charSequence The text to process.
+ * @return The color inverted text.
+ */
+ public CharSequence invertCharSequenceColors(CharSequence charSequence) {
+ if (charSequence instanceof Spanned) {
+ Spanned ss = (Spanned) charSequence;
+ Object[] spans = ss.getSpans(0, ss.length(), Object.class);
+ SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
+ for (Object span : spans) {
+ Object resultSpan = span;
+ if (span instanceof TextAppearanceSpan) {
+ resultSpan = processTextAppearanceSpan((TextAppearanceSpan) span);
+ }
+ builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
+ ss.getSpanFlags(span));
+ }
+ return builder;
+ }
+ return charSequence;
+ }
+
+ private TextAppearanceSpan processTextAppearanceSpan(TextAppearanceSpan span) {
+ ColorStateList colorStateList = span.getTextColor();
+ if (colorStateList != null) {
+ int[] colors = colorStateList.getColors();
+ boolean changed = false;
+ for (int i = 0; i < colors.length; i++) {
+ if (ImageUtils.isGrayscale(colors[i])) {
+
+ // Allocate a new array so we don't change the colors in the old color state
+ // list.
+ if (!changed) {
+ colors = Arrays.copyOf(colors, colors.length);
+ }
+ colors[i] = processColor(colors[i]);
+ changed = true;
+ }
+ }
+ if (changed) {
+ return new TextAppearanceSpan(
+ span.getFamily(), span.getTextStyle(), span.getTextSize(),
+ new ColorStateList(colorStateList.getStates(), colors),
+ span.getLinkTextColor());
+ }
+ }
+ return span;
+ }
+
+ private int processColor(int color) {
+ return Color.argb(Color.alpha(color),
+ 255 - Color.red(color),
+ 255 - Color.green(color),
+ 255 - Color.blue(color));
+ }
+}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index a54b364..c0d1e88 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -30,7 +30,7 @@ public class Preconditions {
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
- public static <T> T checkNotNull(T reference) {
+ public static <T> T checkNotNull(final T reference) {
if (reference == null) {
throw new NullPointerException();
}
@@ -47,7 +47,7 @@ public class Preconditions {
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
- public static <T> T checkNotNull(T reference, Object errorMessage) {
+ public static <T> T checkNotNull(final T reference, final Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
}
@@ -61,7 +61,7 @@ public class Preconditions {
* @param expression a boolean expression
* @throws IllegalStateException if {@code expression} is false
*/
- public static void checkState(boolean expression) {
+ public static void checkState(final boolean expression) {
if (!expression) {
throw new IllegalStateException();
}
@@ -71,11 +71,205 @@ public class Preconditions {
* Check the requested flags, throwing if any requested flags are outside
* the allowed set.
*/
- public static void checkFlagsArgument(int requestedFlags, int allowedFlags) {
+ public static void checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
if ((requestedFlags & allowedFlags) != requestedFlags) {
throw new IllegalArgumentException("Requested flags 0x"
+ Integer.toHexString(requestedFlags) + ", but only 0x"
+ Integer.toHexString(allowedFlags) + " are allowed");
}
}
+
+ /**
+ * Ensures that that the argument numeric value is non-negative.
+ *
+ * @param value a numeric int value
+ * @param errorMessage the exception message to use if the check fails
+ * @return the validated numeric value
+ * @throws IllegalArgumentException if {@code value} was negative
+ */
+ public static int checkArgumentNonnegative(final int value, final String errorMessage) {
+ if (value < 0) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that that the argument numeric value is non-negative.
+ *
+ * @param value a numeric long value
+ * @param errorMessage the exception message to use if the check fails
+ * @return the validated numeric value
+ * @throws IllegalArgumentException if {@code value} was negative
+ */
+ public static long checkArgumentNonnegative(final long value, final String errorMessage) {
+ if (value < 0) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that that the argument numeric value is positive.
+ *
+ * @param value a numeric int value
+ * @param errorMessage the exception message to use if the check fails
+ * @return the validated numeric value
+ * @throws IllegalArgumentException if {@code value} was not positive
+ */
+ public static int checkArgumentPositive(final int value, final String errorMessage) {
+ if (value <= 0) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that the argument floating point value is a finite number.
+ *
+ * <p>A finite number is defined to be both representable (that is, not NaN) and
+ * not infinite (that is neither positive or negative infinity).</p>
+ *
+ * @param value a floating point value
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated floating point value
+ *
+ * @throws IllegalArgumentException if {@code value} was not finite
+ */
+ public static float checkArgumentFinite(final float value, final String valueName) {
+ if (Float.isNaN(value)) {
+ throw new IllegalArgumentException(valueName + " must not be NaN");
+ } else if (Float.isInfinite(value)) {
+ throw new IllegalArgumentException(valueName + " must not be infinite");
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that the argument floating point value is within the inclusive range.
+ *
+ * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
+ * will always be out of range.</p>
+ *
+ * @param value a floating point value
+ * @param lower the lower endpoint of the inclusive range
+ * @param upper the upper endpoint of the inclusive range
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated floating point value
+ *
+ * @throws IllegalArgumentException if {@code value} was not within the range
+ */
+ public static float checkArgumentInRange(float value, float lower, float upper,
+ String valueName) {
+ if (Float.isNaN(value)) {
+ throw new IllegalArgumentException(valueName + " must not be NaN");
+ } else if (value < lower) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%f, %f] (too low)", valueName, lower, upper));
+ } else if (value > upper) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%f, %f] (too high)", valueName, lower, upper));
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that the argument int value is within the inclusive range.
+ *
+ * @param value a int value
+ * @param lower the lower endpoint of the inclusive range
+ * @param upper the upper endpoint of the inclusive range
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated int value
+ *
+ * @throws IllegalArgumentException if {@code value} was not within the range
+ */
+ public static int checkArgumentInRange(int value, int lower, int upper,
+ String valueName) {
+ if (value < lower) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
+ } else if (value > upper) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that the array is not {@code null}, and none if its elements are {@code null}.
+ *
+ * @param value an array of boxed objects
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated array
+ *
+ * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
+ */
+ public static <T> T[] checkArrayElementsNotNull(final T[] value, final String valueName) {
+ if (value == null) {
+ throw new NullPointerException(valueName + " must not be null");
+ }
+
+ for (int i = 0; i < value.length; ++i) {
+ if (value[i] == null) {
+ throw new NullPointerException(
+ String.format("%s[%d] must not be null", valueName, i));
+ }
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that all elements in the argument floating point array are within the inclusive range
+ *
+ * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
+ * will always be out of range.</p>
+ *
+ * @param value a floating point array of values
+ * @param lower the lower endpoint of the inclusive range
+ * @param upper the upper endpoint of the inclusive range
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated floating point value
+ *
+ * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
+ * @throws NullPointerException if the {@code value} was {@code null}
+ */
+ public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
+ String valueName) {
+ checkNotNull(value, valueName + " must not be null");
+
+ for (int i = 0; i < value.length; ++i) {
+ float v = value[i];
+
+ if (Float.isNaN(v)) {
+ throw new IllegalArgumentException(valueName + "[" + i + "] must not be NaN");
+ } else if (v < lower) {
+ throw new IllegalArgumentException(
+ String.format("%s[%d] is out of range of [%f, %f] (too low)",
+ valueName, i, lower, upper));
+ } else if (v > upper) {
+ throw new IllegalArgumentException(
+ String.format("%s[%d] is out of range of [%f, %f] (too high)",
+ valueName, i, lower, upper));
+ }
+ }
+
+ return value;
+ }
}
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index b380403..af966b1 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -46,6 +46,10 @@ public class Protocol {
public static final int BASE_WIFI_MONITOR = 0x00024000;
public static final int BASE_WIFI_MANAGER = 0x00025000;
public static final int BASE_WIFI_CONTROLLER = 0x00026000;
+ public static final int BASE_WIFI_SCANNER = 0x00027000;
+ public static final int BASE_WIFI_SCANNER_SERVICE = 0x00027100;
+ public static final int BASE_WIFI_PASSPOINT_MANAGER = 0x00028000;
+ public static final int BASE_WIFI_PASSPOINT_SERVICE = 0x00028100;
public static final int BASE_DHCP = 0x00030000;
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
@@ -53,5 +57,9 @@ public class Protocol {
public static final int BASE_DNS_PINGER = 0x00050000;
public static final int BASE_NSD_MANAGER = 0x00060000;
public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000;
+ public static final int BASE_CONNECTIVITY_MANAGER = 0x00080000;
+ public static final int BASE_NETWORK_AGENT = 0x00081000;
+ public static final int BASE_NETWORK_MONITOR = 0x00082000;
+ public static final int BASE_NETWORK_FACTORY = 0x00083000;
//TODO: define all used protocols
}
diff --git a/core/java/com/android/internal/util/VirtualRefBasePtr.java b/core/java/com/android/internal/util/VirtualRefBasePtr.java
new file mode 100644
index 0000000..52306f1
--- /dev/null
+++ b/core/java/com/android/internal/util/VirtualRefBasePtr.java
@@ -0,0 +1,53 @@
+/*
+ * 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.internal.util;
+
+/**
+ * Helper class that contains a strong reference to a VirtualRefBase native
+ * object. This will incStrong in the ctor, and decStrong in the finalizer
+ */
+public final class VirtualRefBasePtr {
+ private long mNativePtr;
+
+ public VirtualRefBasePtr(long ptr) {
+ mNativePtr = ptr;
+ nIncStrong(mNativePtr);
+ }
+
+ public long get() {
+ return mNativePtr;
+ }
+
+ public void release() {
+ if (mNativePtr != 0) {
+ nDecStrong(mNativePtr);
+ mNativePtr = 0;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ release();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private static native void nIncStrong(long ptr);
+ private static native void nDecStrong(long ptr);
+}
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index b35de93..dca9921 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -220,28 +220,74 @@ public class XmlUtils {
* @see #readMapXml
*/
public static final void writeMapXml(Map val, String name, XmlSerializer out)
- throws XmlPullParserException, java.io.IOException
- {
+ throws XmlPullParserException, java.io.IOException {
+ writeMapXml(val, name, out, null);
+ }
+
+ /**
+ * Flatten a Map into an XmlSerializer. The map can later be read back
+ * with readThisMapXml().
+ *
+ * @param val The map to be flattened.
+ * @param name Name attribute to include with this list's tag, or null for
+ * none.
+ * @param out XmlSerializer to write the map into.
+ * @param callback Method to call when an Object type is not recognized.
+ *
+ * @see #writeMapXml(Map, OutputStream)
+ * @see #writeListXml
+ * @see #writeValueXml
+ * @see #readMapXml
+ *
+ * @hide
+ */
+ public static final void writeMapXml(Map val, String name, XmlSerializer out,
+ WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
+
if (val == null) {
out.startTag(null, "null");
out.endTag(null, "null");
return;
}
- Set s = val.entrySet();
- Iterator i = s.iterator();
-
out.startTag(null, "map");
if (name != null) {
out.attribute(null, "name", name);
}
+ writeMapXml(val, out, callback);
+
+ out.endTag(null, "map");
+ }
+
+ /**
+ * Flatten a Map into an XmlSerializer. The map can later be read back
+ * with readThisMapXml(). This method presumes that the start tag and
+ * name attribute have already been written and does not write an end tag.
+ *
+ * @param val The map to be flattened.
+ * @param out XmlSerializer to write the map into.
+ *
+ * @see #writeMapXml(Map, OutputStream)
+ * @see #writeListXml
+ * @see #writeValueXml
+ * @see #readMapXml
+ *
+ * @hide
+ */
+ public static final void writeMapXml(Map val, XmlSerializer out,
+ WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
+ if (val == null) {
+ return;
+ }
+
+ Set s = val.entrySet();
+ Iterator i = s.iterator();
+
while (i.hasNext()) {
Map.Entry e = (Map.Entry)i.next();
- writeValueXml(e.getValue(), (String)e.getKey(), out);
+ writeValueXml(e.getValue(), (String)e.getKey(), out, callback);
}
-
- out.endTag(null, "map");
}
/**
@@ -387,6 +433,123 @@ public class XmlUtils {
}
/**
+ * Flatten a long[] into an XmlSerializer. The list can later be read back
+ * with readThisLongArrayXml().
+ *
+ * @param val The long array to be flattened.
+ * @param name Name attribute to include with this array's tag, or null for
+ * none.
+ * @param out XmlSerializer to write the array into.
+ *
+ * @see #writeMapXml
+ * @see #writeValueXml
+ * @see #readThisIntArrayXml
+ */
+ public static final void writeLongArrayXml(long[] val, String name, XmlSerializer out)
+ throws XmlPullParserException, java.io.IOException {
+
+ if (val == null) {
+ out.startTag(null, "null");
+ out.endTag(null, "null");
+ return;
+ }
+
+ out.startTag(null, "long-array");
+ if (name != null) {
+ out.attribute(null, "name", name);
+ }
+
+ final int N = val.length;
+ out.attribute(null, "num", Integer.toString(N));
+
+ for (int i=0; i<N; i++) {
+ out.startTag(null, "item");
+ out.attribute(null, "value", Long.toString(val[i]));
+ out.endTag(null, "item");
+ }
+
+ out.endTag(null, "long-array");
+ }
+
+ /**
+ * Flatten a double[] into an XmlSerializer. The list can later be read back
+ * with readThisDoubleArrayXml().
+ *
+ * @param val The double array to be flattened.
+ * @param name Name attribute to include with this array's tag, or null for
+ * none.
+ * @param out XmlSerializer to write the array into.
+ *
+ * @see #writeMapXml
+ * @see #writeValueXml
+ * @see #readThisIntArrayXml
+ */
+ public static final void writeDoubleArrayXml(double[] val, String name, XmlSerializer out)
+ throws XmlPullParserException, java.io.IOException {
+
+ if (val == null) {
+ out.startTag(null, "null");
+ out.endTag(null, "null");
+ return;
+ }
+
+ out.startTag(null, "double-array");
+ if (name != null) {
+ out.attribute(null, "name", name);
+ }
+
+ final int N = val.length;
+ out.attribute(null, "num", Integer.toString(N));
+
+ for (int i=0; i<N; i++) {
+ out.startTag(null, "item");
+ out.attribute(null, "value", Double.toString(val[i]));
+ out.endTag(null, "item");
+ }
+
+ out.endTag(null, "double-array");
+ }
+
+ /**
+ * Flatten a String[] into an XmlSerializer. The list can later be read back
+ * with readThisStringArrayXml().
+ *
+ * @param val The long array to be flattened.
+ * @param name Name attribute to include with this array's tag, or null for
+ * none.
+ * @param out XmlSerializer to write the array into.
+ *
+ * @see #writeMapXml
+ * @see #writeValueXml
+ * @see #readThisIntArrayXml
+ */
+ public static final void writeStringArrayXml(String[] val, String name, XmlSerializer out)
+ throws XmlPullParserException, java.io.IOException {
+
+ if (val == null) {
+ out.startTag(null, "null");
+ out.endTag(null, "null");
+ return;
+ }
+
+ out.startTag(null, "string-array");
+ if (name != null) {
+ out.attribute(null, "name", name);
+ }
+
+ final int N = val.length;
+ out.attribute(null, "num", Integer.toString(N));
+
+ for (int i=0; i<N; i++) {
+ out.startTag(null, "item");
+ out.attribute(null, "value", val[i]);
+ out.endTag(null, "item");
+ }
+
+ out.endTag(null, "string-array");
+ }
+
+ /**
* Flatten an object's value into an XmlSerializer. The value can later
* be read back with readThisValueXml().
*
@@ -403,8 +566,29 @@ public class XmlUtils {
* @see #readValueXml
*/
public static final void writeValueXml(Object v, String name, XmlSerializer out)
- throws XmlPullParserException, java.io.IOException
- {
+ throws XmlPullParserException, java.io.IOException {
+ writeValueXml(v, name, out, null);
+ }
+
+ /**
+ * Flatten an object's value into an XmlSerializer. The value can later
+ * be read back with readThisValueXml().
+ *
+ * Currently supported value types are: null, String, Integer, Long,
+ * Float, Double Boolean, Map, List.
+ *
+ * @param v The object to be flattened.
+ * @param name Name attribute to include with this value's tag, or null
+ * for none.
+ * @param out XmlSerializer to write the object into.
+ * @param callback Handler for Object types not recognized.
+ *
+ * @see #writeMapXml
+ * @see #writeListXml
+ * @see #readValueXml
+ */
+ private static final void writeValueXml(Object v, String name, XmlSerializer out,
+ WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
String typeStr;
if (v == null) {
out.startTag(null, "null");
@@ -437,14 +621,23 @@ public class XmlUtils {
} else if (v instanceof int[]) {
writeIntArrayXml((int[])v, name, out);
return;
+ } else if (v instanceof long[]) {
+ writeLongArrayXml((long[])v, name, out);
+ return;
+ } else if (v instanceof double[]) {
+ writeDoubleArrayXml((double[])v, name, out);
+ return;
+ } else if (v instanceof String[]) {
+ writeStringArrayXml((String[])v, name, out);
+ return;
} else if (v instanceof Map) {
writeMapXml((Map)v, name, out);
return;
} else if (v instanceof List) {
- writeListXml((List)v, name, out);
+ writeListXml((List) v, name, out);
return;
} else if (v instanceof Set) {
- writeSetXml((Set)v, name, out);
+ writeSetXml((Set) v, name, out);
return;
} else if (v instanceof CharSequence) {
// XXX This is to allow us to at least write something if
@@ -457,6 +650,9 @@ public class XmlUtils {
out.text(v.toString());
out.endTag(null, "string");
return;
+ } else if (callback != null) {
+ callback.writeUnknownObject(v, name, out);
+ return;
} else {
throw new RuntimeException("writeValueXml: unable to write value " + v);
}
@@ -550,14 +746,35 @@ public class XmlUtils {
* @see #readMapXml
*/
public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
- String[] name) throws XmlPullParserException, java.io.IOException
+ String[] name) throws XmlPullParserException, java.io.IOException {
+ return readThisMapXml(parser, endTag, name, null);
+ }
+
+ /**
+ * Read a HashMap object from an XmlPullParser. The XML data could
+ * previously have been generated by writeMapXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the map.
+ *
+ * @param parser The XmlPullParser from which to read the map data.
+ * @param endTag Name of the tag that will end the map, usually "map".
+ * @param name An array of one string, used to return the name attribute
+ * of the map's tag.
+ *
+ * @return HashMap The newly generated map.
+ *
+ * @see #readMapXml
+ * @hide
+ */
+ public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
+ String[] name, ReadMapCallback callback)
+ throws XmlPullParserException, java.io.IOException
{
HashMap<String, Object> map = new HashMap<String, Object>();
int eventType = parser.getEventType();
do {
if (eventType == parser.START_TAG) {
- Object val = readThisValueXml(parser, name);
+ Object val = readThisValueXml(parser, name, callback);
map.put(name[0], val);
} else if (eventType == parser.END_TAG) {
if (parser.getName().equals(endTag)) {
@@ -587,15 +804,34 @@ public class XmlUtils {
*
* @see #readListXml
*/
- public static final ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name)
- throws XmlPullParserException, java.io.IOException
- {
+ public static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
+ String[] name) throws XmlPullParserException, java.io.IOException {
+ return readThisListXml(parser, endTag, name, null);
+ }
+
+ /**
+ * Read an ArrayList object from an XmlPullParser. The XML data could
+ * previously have been generated by writeListXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "list".
+ * @param name An array of one string, used to return the name attribute
+ * of the list's tag.
+ *
+ * @return HashMap The newly generated list.
+ *
+ * @see #readListXml
+ */
+ private static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
+ String[] name, ReadMapCallback callback)
+ throws XmlPullParserException, java.io.IOException {
ArrayList list = new ArrayList();
int eventType = parser.getEventType();
do {
if (eventType == parser.START_TAG) {
- Object val = readThisValueXml(parser, name);
+ Object val = readThisValueXml(parser, name, callback);
list.add(val);
//System.out.println("Adding to list: " + val);
} else if (eventType == parser.END_TAG) {
@@ -611,7 +847,29 @@ public class XmlUtils {
throw new XmlPullParserException(
"Document ended before " + endTag + " end tag");
}
-
+
+ /**
+ * Read a HashSet object from an XmlPullParser. The XML data could previously
+ * have been generated by writeSetXml(). The XmlPullParser must be positioned
+ * <em>after</em> the tag that begins the set.
+ *
+ * @param parser The XmlPullParser from which to read the set data.
+ * @param endTag Name of the tag that will end the set, usually "set".
+ * @param name An array of one string, used to return the name attribute
+ * of the set's tag.
+ *
+ * @return HashSet The newly generated set.
+ *
+ * @throws XmlPullParserException
+ * @throws java.io.IOException
+ *
+ * @see #readSetXml
+ */
+ public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
+ throws XmlPullParserException, java.io.IOException {
+ return readThisSetXml(parser, endTag, name, null);
+ }
+
/**
* Read a HashSet object from an XmlPullParser. The XML data could previously
* have been generated by writeSetXml(). The XmlPullParser must be positioned
@@ -628,15 +886,16 @@ public class XmlUtils {
* @throws java.io.IOException
*
* @see #readSetXml
+ * @hide
*/
- public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
- throws XmlPullParserException, java.io.IOException {
+ private static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name,
+ ReadMapCallback callback) throws XmlPullParserException, java.io.IOException {
HashSet set = new HashSet();
int eventType = parser.getEventType();
do {
if (eventType == parser.START_TAG) {
- Object val = readThisValueXml(parser, name);
+ Object val = readThisValueXml(parser, name, callback);
set.add(val);
//System.out.println("Adding to set: " + val);
} else if (eventType == parser.END_TAG) {
@@ -681,6 +940,7 @@ public class XmlUtils {
throw new XmlPullParserException(
"Not a number in num attribute in byte-array");
}
+ parser.next();
int[] array = new int[num];
int i = 0;
@@ -722,6 +982,187 @@ public class XmlUtils {
}
/**
+ * Read a long[] object from an XmlPullParser. The XML data could
+ * previously have been generated by writeLongArrayXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "list".
+ * @param name An array of one string, used to return the name attribute
+ * of the list's tag.
+ *
+ * @return Returns a newly generated long[].
+ *
+ * @see #readListXml
+ */
+ public static final long[] readThisLongArrayXml(XmlPullParser parser,
+ String endTag, String[] name)
+ throws XmlPullParserException, java.io.IOException {
+
+ int num;
+ try {
+ num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need num attribute in long-array");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in num attribute in long-array");
+ }
+ parser.next();
+
+ long[] array = new long[num];
+ int i = 0;
+
+ int eventType = parser.getEventType();
+ do {
+ if (eventType == parser.START_TAG) {
+ if (parser.getName().equals("item")) {
+ try {
+ array[i] = Long.parseLong(parser.getAttributeValue(null, "value"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need value attribute in item");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in value attribute in item");
+ }
+ } else {
+ throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+ }
+ } else if (eventType == parser.END_TAG) {
+ if (parser.getName().equals(endTag)) {
+ return array;
+ } else if (parser.getName().equals("item")) {
+ i++;
+ } else {
+ throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+ parser.getName());
+ }
+ }
+ eventType = parser.next();
+ } while (eventType != parser.END_DOCUMENT);
+
+ throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+ }
+
+ /**
+ * Read a double[] object from an XmlPullParser. The XML data could
+ * previously have been generated by writeDoubleArrayXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "double-array".
+ * @param name An array of one string, used to return the name attribute
+ * of the list's tag.
+ *
+ * @return Returns a newly generated double[].
+ *
+ * @see #readListXml
+ */
+ public static final double[] readThisDoubleArrayXml(XmlPullParser parser, String endTag,
+ String[] name) throws XmlPullParserException, java.io.IOException {
+
+ int num;
+ try {
+ num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need num attribute in double-array");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in num attribute in double-array");
+ }
+ parser.next();
+
+ double[] array = new double[num];
+ int i = 0;
+
+ int eventType = parser.getEventType();
+ do {
+ if (eventType == parser.START_TAG) {
+ if (parser.getName().equals("item")) {
+ try {
+ array[i] = Double.parseDouble(parser.getAttributeValue(null, "value"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need value attribute in item");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in value attribute in item");
+ }
+ } else {
+ throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+ }
+ } else if (eventType == parser.END_TAG) {
+ if (parser.getName().equals(endTag)) {
+ return array;
+ } else if (parser.getName().equals("item")) {
+ i++;
+ } else {
+ throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+ parser.getName());
+ }
+ }
+ eventType = parser.next();
+ } while (eventType != parser.END_DOCUMENT);
+
+ throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+ }
+
+ /**
+ * Read a String[] object from an XmlPullParser. The XML data could
+ * previously have been generated by writeStringArrayXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "string-array".
+ * @param name An array of one string, used to return the name attribute
+ * of the list's tag.
+ *
+ * @return Returns a newly generated String[].
+ *
+ * @see #readListXml
+ */
+ public static final String[] readThisStringArrayXml(XmlPullParser parser, String endTag,
+ String[] name) throws XmlPullParserException, java.io.IOException {
+
+ int num;
+ try {
+ num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need num attribute in string-array");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in num attribute in string-array");
+ }
+ parser.next();
+
+ String[] array = new String[num];
+ int i = 0;
+
+ int eventType = parser.getEventType();
+ do {
+ if (eventType == parser.START_TAG) {
+ if (parser.getName().equals("item")) {
+ try {
+ array[i] = parser.getAttributeValue(null, "value");
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need value attribute in item");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in value attribute in item");
+ }
+ } else {
+ throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+ }
+ } else if (eventType == parser.END_TAG) {
+ if (parser.getName().equals(endTag)) {
+ return array;
+ } else if (parser.getName().equals("item")) {
+ i++;
+ } else {
+ throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+ parser.getName());
+ }
+ }
+ eventType = parser.next();
+ } while (eventType != parser.END_DOCUMENT);
+
+ throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+ }
+
+ /**
* Read a flattened object from an XmlPullParser. The XML data could
* previously have been written with writeMapXml(), writeListXml(), or
* writeValueXml(). The XmlPullParser must be positioned <em>at</em> the
@@ -743,7 +1184,7 @@ public class XmlUtils {
int eventType = parser.getEventType();
do {
if (eventType == parser.START_TAG) {
- return readThisValueXml(parser, name);
+ return readThisValueXml(parser, name, null);
} else if (eventType == parser.END_TAG) {
throw new XmlPullParserException(
"Unexpected end tag at: " + parser.getName());
@@ -758,9 +1199,8 @@ public class XmlUtils {
"Unexpected end of document");
}
- private static final Object readThisValueXml(XmlPullParser parser, String[] name)
- throws XmlPullParserException, java.io.IOException
- {
+ private static final Object readThisValueXml(XmlPullParser parser, String[] name,
+ ReadMapCallback callback) throws XmlPullParserException, java.io.IOException {
final String valueName = parser.getAttributeValue(null, "name");
final String tagName = parser.getName();
@@ -794,11 +1234,25 @@ public class XmlUtils {
} else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
// all work already done by readThisPrimitiveValueXml
} else if (tagName.equals("int-array")) {
- parser.next();
res = readThisIntArrayXml(parser, "int-array", name);
name[0] = valueName;
//System.out.println("Returning value for " + valueName + ": " + res);
return res;
+ } else if (tagName.equals("long-array")) {
+ res = readThisLongArrayXml(parser, "long-array", name);
+ name[0] = valueName;
+ //System.out.println("Returning value for " + valueName + ": " + res);
+ return res;
+ } else if (tagName.equals("double-array")) {
+ res = readThisDoubleArrayXml(parser, "double-array", name);
+ name[0] = valueName;
+ //System.out.println("Returning value for " + valueName + ": " + res);
+ return res;
+ } else if (tagName.equals("string-array")) {
+ res = readThisStringArrayXml(parser, "string-array", name);
+ name[0] = valueName;
+ //System.out.println("Returning value for " + valueName + ": " + res);
+ return res;
} else if (tagName.equals("map")) {
parser.next();
res = readThisMapXml(parser, "map", name);
@@ -817,9 +1271,12 @@ public class XmlUtils {
name[0] = valueName;
//System.out.println("Returning value for " + valueName + ": " + res);
return res;
+ } else if (callback != null) {
+ res = callback.readThisUnknownObjectXml(parser, tagName);
+ name[0] = valueName;
+ return res;
} else {
- throw new XmlPullParserException(
- "Unknown tag: " + tagName);
+ throw new XmlPullParserException("Unknown tag: " + tagName);
}
// Skip through to end tag.
@@ -912,6 +1369,15 @@ public class XmlUtils {
}
}
+ public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
+ final String value = in.getAttributeValue(null, name);
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
final String value = in.getAttributeValue(null, name);
try {
@@ -958,4 +1424,39 @@ public class XmlUtils {
throws IOException {
out.attribute(null, name, Boolean.toString(value));
}
+
+ /** @hide */
+ public interface WriteMapCallback {
+ /**
+ * Called from writeMapXml when an Object type is not recognized. The implementer
+ * must write out the entire element including start and end tags.
+ *
+ * @param v The object to be written out
+ * @param name The mapping key for v. Must be written into the "name" attribute of the
+ * start tag.
+ * @param out The XML output stream.
+ * @throws XmlPullParserException on unrecognized Object type.
+ * @throws IOException on XmlSerializer serialization errors.
+ * @hide
+ */
+ public void writeUnknownObject(Object v, String name, XmlSerializer out)
+ throws XmlPullParserException, IOException;
+ }
+
+ /** @hide */
+ public interface ReadMapCallback {
+ /**
+ * Called from readThisMapXml when a START_TAG is not recognized. The input stream
+ * is positioned within the start tag so that attributes can be read using in.getAttribute.
+ *
+ * @param in the XML input stream
+ * @param tag the START_TAG that was not recognized.
+ * @return the Object parsed from the stream which will be put into the map.
+ * @throws XmlPullParserException if the START_TAG is not recognized.
+ * @throws IOException on XmlPullParser serialization errors.
+ * @hide
+ */
+ public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
+ throws XmlPullParserException, IOException;
+ }
}
diff --git a/core/java/com/android/internal/view/ActionBarPolicy.java b/core/java/com/android/internal/view/ActionBarPolicy.java
index 25086c5..bee59dc 100644
--- a/core/java/com/android/internal/view/ActionBarPolicy.java
+++ b/core/java/com/android/internal/view/ActionBarPolicy.java
@@ -19,11 +19,9 @@ package com.android.internal.view;
import com.android.internal.R;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
-import android.view.ViewConfiguration;
/**
* Allows components to query for various configuration policy decisions
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index ce4312d..9e8d12b 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -27,4 +27,5 @@ oneway interface IInputMethodClient {
void onBindMethod(in InputBindResult res);
void onUnbindMethod(int sequence);
void setActive(boolean active);
+ void setCursorAnchorMonitorMode(int monitorMode);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 3724a08..5336174 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -32,7 +32,9 @@ import com.android.internal.view.IInputMethodClient;
* this file.
*/
interface IInputMethodManager {
+ // TODO: Use ParceledListSlice instead
List<InputMethodInfo> getInputMethodList();
+ // TODO: Use ParceledListSlice instead
List<InputMethodInfo> getEnabledInputMethodList();
List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId,
boolean allowsImplicitlySelectedSubtypes);
@@ -74,4 +76,7 @@ interface IInputMethodManager {
boolean shouldOfferSwitchingToNextInputMethod(in IBinder token);
boolean setInputMethodEnabled(String id, boolean enabled);
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
+ int getInputMethodWindowVisibleHeight();
+ oneway void notifyTextCommitted();
+ void setCursorAnchorMonitorMode(in IBinder token, int monitorMode);
}
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index 90210ce..367b713 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -21,6 +21,7 @@ import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.ExtractedText;
/**
@@ -47,4 +48,6 @@ oneway interface IInputMethodSession {
void toggleSoftInput(int showFlags, int hideFlags);
void finishSession();
+
+ void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo);
}
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index 6295314..b479cb1 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -18,7 +18,9 @@ package com.android.internal.view;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.graphics.Point;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
@@ -26,15 +28,20 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
+import android.view.Display;
import android.view.IWindowManager;
import android.view.Surface;
import android.view.WindowManagerGlobal;
+import com.android.internal.R;
+
/**
* Provides helper functions for configuring the display rotation policy.
*/
public final class RotationPolicy {
private static final String TAG = "RotationPolicy";
+ private static final int CURRENT_ROTATION = -1;
+ private static final int NATURAL_ROTATION = Surface.ROTATION_0;
private RotationPolicy() {
}
@@ -57,23 +64,33 @@ public final class RotationPolicy {
}
/**
- * Returns true if the device supports the rotation-lock toggle feature
- * in the system UI or system bar.
+ * Returns the orientation that will be used when locking the orientation from system UI
+ * with {@link #setRotationLock}.
*
- * When the rotation-lock toggle is supported, the "auto-rotate screen" option in
- * Display settings should be hidden, but it should remain available in Accessibility
- * settings.
+ * If the device only supports locking to its natural orientation, this will be either
+ * Configuration.ORIENTATION_PORTRAIT or Configuration.ORIENTATION_LANDSCAPE,
+ * otherwise Configuration.ORIENTATION_UNDEFINED if any orientation is lockable.
*/
- public static boolean isRotationLockToggleSupported(Context context) {
- return isRotationSupported(context)
- && context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
+ public static int getRotationLockOrientation(Context context) {
+ if (!areAllRotationsAllowed(context)) {
+ final Point size = new Point();
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ try {
+ wm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, size);
+ return size.x < size.y ?
+ Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to get the display size");
+ }
+ }
+ return Configuration.ORIENTATION_UNDEFINED;
}
/**
- * Returns true if the rotation-lock toggle should be shown in the UI.
+ * Returns true if the rotation-lock toggle should be shown in system UI.
*/
public static boolean isRotationLockToggleVisible(Context context) {
- return isRotationLockToggleSupported(context) &&
+ return isRotationSupported(context) &&
Settings.System.getIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
UserHandle.USER_CURRENT) == 0;
@@ -88,50 +105,42 @@ public final class RotationPolicy {
}
/**
- * Enables or disables rotation lock.
- *
- * Should be used by the rotation lock toggle.
+ * Enables or disables rotation lock from the system UI toggle.
*/
public static void setRotationLock(Context context, final boolean enabled) {
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
UserHandle.USER_CURRENT);
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- try {
- IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
- if (enabled) {
- wm.freezeRotation(-1);
- } else {
- wm.thawRotation();
- }
- } catch (RemoteException exc) {
- Log.w(TAG, "Unable to save auto-rotate setting");
- }
- }
- });
+ final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
+ setRotationLock(enabled, rotation);
}
/**
- * Enables or disables rotation lock and adjusts whether the rotation lock toggle
- * should be hidden for accessibility purposes.
+ * Enables or disables natural rotation lock from Accessibility settings.
*
- * Should be used by Display settings and Accessibility settings.
+ * If rotation is locked for accessibility, the system UI toggle is hidden to avoid confusion.
*/
public static void setRotationLockForAccessibility(Context context, final boolean enabled) {
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
+ setRotationLock(enabled, NATURAL_ROTATION);
+ }
+
+ private static boolean areAllRotationsAllowed(Context context) {
+ return context.getResources().getBoolean(R.bool.config_allowAllRotations);
+ }
+
+ private static void setRotationLock(final boolean enabled, final int rotation) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
if (enabled) {
- wm.freezeRotation(Surface.ROTATION_0);
+ wm.freezeRotation(rotation);
} else {
wm.thawRotation();
}
diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
new file mode 100644
index 0000000..06838c9
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
@@ -0,0 +1,75 @@
+/*
+ * 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.internal.view.animation;
+
+import android.animation.TimeInterpolator;
+import android.util.TimeUtils;
+import android.view.Choreographer;
+
+/**
+ * Interpolator that builds a lookup table to use. This is a fallback for
+ * building a native interpolator from a TimeInterpolator that is not marked
+ * with {@link HasNativeInterpolator}
+ *
+ * This implements TimeInterpolator to allow for easier interop with Animators
+ */
+@HasNativeInterpolator
+public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeInterpolator {
+
+ private TimeInterpolator mSourceInterpolator;
+ private final float mLut[];
+
+ /**
+ * Used to cache the float[] LUT for use across multiple native
+ * interpolator creation
+ */
+ public FallbackLUTInterpolator(TimeInterpolator interpolator, long duration) {
+ mSourceInterpolator = interpolator;
+ mLut = createLUT(interpolator, duration);
+ }
+
+ private static float[] createLUT(TimeInterpolator interpolator, long duration) {
+ long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
+ int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
+ int numAnimFrames = (int) Math.ceil(duration / animIntervalMs);
+ float values[] = new float[numAnimFrames];
+ float lastFrame = numAnimFrames - 1;
+ for (int i = 0; i < numAnimFrames; i++) {
+ float inValue = i / lastFrame;
+ values[i] = interpolator.getInterpolation(inValue);
+ }
+ return values;
+ }
+
+ @Override
+ public long createNativeInterpolator() {
+ return NativeInterpolatorFactoryHelper.createLutInterpolator(mLut);
+ }
+
+ /**
+ * Used to create a one-shot float[] LUT & native interpolator
+ */
+ public static long createNativeInterpolator(TimeInterpolator interpolator, long duration) {
+ float[] lut = createLUT(interpolator, duration);
+ return NativeInterpolatorFactoryHelper.createLutInterpolator(lut);
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ return mSourceInterpolator.getInterpolation(input);
+ }
+}
diff --git a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java b/core/java/com/android/internal/view/animation/HasNativeInterpolator.java
new file mode 100644
index 0000000..48ea4da
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/HasNativeInterpolator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.internal.view.animation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This is a class annotation that signals that it is safe to create
+ * a native interpolator counterpart via {@link NativeInterpolatorFactory}
+ *
+ * The idea here is to prevent subclasses of interpolators from being treated as a
+ * NativeInterpolatorFactory, and instead have them fall back to the LUT & LERP
+ * method like a custom interpolator.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface HasNativeInterpolator {
+}
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
new file mode 100644
index 0000000..fcacd52
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
@@ -0,0 +1,21 @@
+/*
+ * 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.internal.view.animation;
+
+public interface NativeInterpolatorFactory {
+ long createNativeInterpolator();
+}
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
new file mode 100644
index 0000000..7cd75f3
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.internal.view.animation;
+
+/**
+ * Static utility class for constructing native interpolators to keep the
+ * JNI simpler
+ */
+public final class NativeInterpolatorFactoryHelper {
+ private NativeInterpolatorFactoryHelper() {}
+
+ public static native long createAccelerateDecelerateInterpolator();
+ public static native long createAccelerateInterpolator(float factor);
+ public static native long createAnticipateInterpolator(float tension);
+ public static native long createAnticipateOvershootInterpolator(float tension);
+ public static native long createBounceInterpolator();
+ public static native long createCycleInterpolator(float cycles);
+ public static native long createDecelerateInterpolator(float factor);
+ public static native long createLinearInterpolator();
+ public static native long createOvershootInterpolator(float tension);
+ public static native long createLutInterpolator(float[] values);
+}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
index 7ca6c1b..ed676bb 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItem.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -163,7 +163,7 @@ public class ActionMenuItem implements MenuItem {
public MenuItem setIcon(int iconRes) {
mIconResId = iconRes;
- mIconDrawable = mContext.getResources().getDrawable(iconRes);
+ mIconDrawable = mContext.getDrawable(iconRes);
return this;
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index 238a9c0..891baea 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -28,8 +28,11 @@ import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.ActionMenuView;
+import android.widget.ListPopupWindow;
import android.widget.TextView;
import android.widget.Toast;
+import android.widget.ListPopupWindow.ForwardingListener;
/**
* @hide
@@ -43,6 +46,8 @@ public class ActionMenuItemView extends TextView
private CharSequence mTitle;
private Drawable mIcon;
private MenuBuilder.ItemInvoker mItemInvoker;
+ private ForwardingListener mForwardingListener;
+ private PopupCallback mPopupCallback;
private boolean mAllowTextWithIcon;
private boolean mExpandedFormat;
@@ -60,13 +65,17 @@ public class ActionMenuItemView extends TextView
this(context, attrs, 0);
}
- public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public ActionMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public ActionMenuItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
final Resources res = context.getResources();
mAllowTextWithIcon = res.getBoolean(
com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon);
- TypedArray a = context.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.ActionMenuItemView, 0, 0);
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.ActionMenuItemView, defStyleAttr, defStyleRes);
mMinWidth = a.getDimensionPixelSize(
com.android.internal.R.styleable.ActionMenuItemView_minWidth, 0);
a.recycle();
@@ -99,6 +108,7 @@ public class ActionMenuItemView extends TextView
return mItemData;
}
+ @Override
public void initialize(MenuItemImpl itemData, int menuType) {
mItemData = itemData;
@@ -108,8 +118,24 @@ public class ActionMenuItemView extends TextView
setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
setEnabled(itemData.isEnabled());
+
+ if (itemData.hasSubMenu()) {
+ if (mForwardingListener == null) {
+ mForwardingListener = new ActionMenuItemForwardingListener();
+ }
+ }
}
+ @Override
+ public boolean onTouchEvent(MotionEvent e) {
+ if (mItemData.hasSubMenu() && mForwardingListener != null
+ && mForwardingListener.onTouch(this, e)) {
+ return true;
+ }
+ return super.onTouchEvent(e);
+ }
+
+ @Override
public void onClick(View v) {
if (mItemInvoker != null) {
mItemInvoker.invokeItem(mItemData);
@@ -120,6 +146,10 @@ public class ActionMenuItemView extends TextView
mItemInvoker = invoker;
}
+ public void setPopupCallback(PopupCallback popupCallback) {
+ mPopupCallback = popupCallback;
+ }
+
public boolean prefersCondensedTitle() {
return true;
}
@@ -252,11 +282,6 @@ public class ActionMenuItemView extends TextView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
- // Fill all available height.
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
- }
final boolean textVisible = hasText();
if (textVisible && mSavedPaddingLeft >= 0) {
super.setPadding(mSavedPaddingLeft, getPaddingTop(),
@@ -285,4 +310,42 @@ public class ActionMenuItemView extends TextView
super.setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom());
}
}
+
+ private class ActionMenuItemForwardingListener extends ForwardingListener {
+ public ActionMenuItemForwardingListener() {
+ super(ActionMenuItemView.this);
+ }
+
+ @Override
+ public ListPopupWindow getPopup() {
+ if (mPopupCallback != null) {
+ return mPopupCallback.getPopup();
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean onForwardingStarted() {
+ // Call the invoker, then check if the expected popup is showing.
+ if (mItemInvoker != null && mItemInvoker.invokeItem(mItemData)) {
+ final ListPopupWindow popup = getPopup();
+ return popup != null && popup.isShowing();
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean onForwardingStopped() {
+ final ListPopupWindow popup = getPopup();
+ if (popup != null) {
+ popup.dismiss();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public static abstract class PopupCallback {
+ public abstract ListPopupWindow getPopup();
+ }
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
deleted file mode 100644
index fe1cf72..0000000
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.transition.Transition;
-import android.transition.TransitionManager;
-import android.util.SparseBooleanArray;
-import android.view.ActionProvider;
-import android.view.Gravity;
-import android.view.MenuItem;
-import android.view.SoundEffectConstants;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.ImageButton;
-import android.widget.ListPopupWindow;
-import android.widget.ListPopupWindow.ForwardingListener;
-import com.android.internal.transition.ActionBarTransition;
-import com.android.internal.view.ActionBarPolicy;
-import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView;
-
-import java.util.ArrayList;
-
-/**
- * MenuPresenter for building action menus as seen in the action bar and action modes.
- */
-public class ActionMenuPresenter extends BaseMenuPresenter
- implements ActionProvider.SubUiVisibilityListener {
- private static final String TAG = "ActionMenuPresenter";
-
- private View mOverflowButton;
- private boolean mReserveOverflow;
- private boolean mReserveOverflowSet;
- private int mWidthLimit;
- private int mActionItemWidthLimit;
- private int mMaxItems;
- private boolean mMaxItemsSet;
- private boolean mStrictWidthLimit;
- private boolean mWidthLimitSet;
- private boolean mExpandedActionViewsExclusive;
-
- private int mMinCellSize;
-
- // Group IDs that have been added as actions - used temporarily, allocated here for reuse.
- private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
-
- private View mScrapActionButtonView;
-
- private OverflowPopup mOverflowPopup;
- private ActionButtonSubmenu mActionButtonPopup;
-
- private OpenOverflowRunnable mPostedOpenRunnable;
-
- final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
- int mOpenSubMenuId;
-
- public ActionMenuPresenter(Context context) {
- super(context, com.android.internal.R.layout.action_menu_layout,
- com.android.internal.R.layout.action_menu_item_layout);
- }
-
- @Override
- public void initForMenu(Context context, MenuBuilder menu) {
- super.initForMenu(context, menu);
-
- final Resources res = context.getResources();
-
- final ActionBarPolicy abp = ActionBarPolicy.get(context);
- if (!mReserveOverflowSet) {
- mReserveOverflow = abp.showsOverflowMenuButton();
- }
-
- if (!mWidthLimitSet) {
- mWidthLimit = abp.getEmbeddedMenuWidthLimit();
- }
-
- // Measure for initial configuration
- if (!mMaxItemsSet) {
- mMaxItems = abp.getMaxActionButtons();
- }
-
- int width = mWidthLimit;
- if (mReserveOverflow) {
- if (mOverflowButton == null) {
- mOverflowButton = new OverflowMenuButton(mSystemContext);
- final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- mOverflowButton.measure(spec, spec);
- }
- width -= mOverflowButton.getMeasuredWidth();
- } else {
- mOverflowButton = null;
- }
-
- mActionItemWidthLimit = width;
-
- mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density);
-
- // Drop a scrap view as it may no longer reflect the proper context/config.
- mScrapActionButtonView = null;
- }
-
- public void onConfigurationChanged(Configuration newConfig) {
- if (!mMaxItemsSet) {
- mMaxItems = mContext.getResources().getInteger(
- com.android.internal.R.integer.max_action_buttons);
- }
- if (mMenu != null) {
- mMenu.onItemsChanged(true);
- }
- }
-
- public void setWidthLimit(int width, boolean strict) {
- mWidthLimit = width;
- mStrictWidthLimit = strict;
- mWidthLimitSet = true;
- }
-
- public void setReserveOverflow(boolean reserveOverflow) {
- mReserveOverflow = reserveOverflow;
- mReserveOverflowSet = true;
- }
-
- public void setItemLimit(int itemCount) {
- mMaxItems = itemCount;
- mMaxItemsSet = true;
- }
-
- public void setExpandedActionViewsExclusive(boolean isExclusive) {
- mExpandedActionViewsExclusive = isExclusive;
- }
-
- @Override
- public MenuView getMenuView(ViewGroup root) {
- MenuView result = super.getMenuView(root);
- ((ActionMenuView) result).setPresenter(this);
- return result;
- }
-
- @Override
- public View getItemView(final MenuItemImpl item, View convertView, ViewGroup parent) {
- View actionView = item.getActionView();
- if (actionView == null || item.hasCollapsibleActionView()) {
- actionView = super.getItemView(item, convertView, parent);
- }
- actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE);
-
- final ActionMenuView menuParent = (ActionMenuView) parent;
- final ViewGroup.LayoutParams lp = actionView.getLayoutParams();
- if (!menuParent.checkLayoutParams(lp)) {
- actionView.setLayoutParams(menuParent.generateLayoutParams(lp));
- }
- return actionView;
- }
-
- @Override
- public void bindItemView(final MenuItemImpl item, MenuView.ItemView itemView) {
- itemView.initialize(item, 0);
-
- final ActionMenuView menuView = (ActionMenuView) mMenuView;
- final ActionMenuItemView actionItemView = (ActionMenuItemView) itemView;
- actionItemView.setItemInvoker(menuView);
-
- if (item.hasSubMenu()) {
- actionItemView.setOnTouchListener(new ForwardingListener(actionItemView) {
- @Override
- public ListPopupWindow getPopup() {
- return mActionButtonPopup != null ? mActionButtonPopup.getPopup() : null;
- }
-
- @Override
- protected boolean onForwardingStarted() {
- return onSubMenuSelected((SubMenuBuilder) item.getSubMenu());
- }
-
- @Override
- protected boolean onForwardingStopped() {
- return dismissPopupMenus();
- }
- });
- } else {
- actionItemView.setOnTouchListener(null);
- }
- }
-
- @Override
- public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
- return item.isActionButton();
- }
-
- @Override
- public void updateMenuView(boolean cleared) {
- final ViewGroup menuViewParent = (ViewGroup) ((View) mMenuView).getParent();
- if (menuViewParent != null) {
- ActionBarTransition.beginDelayedTransition(menuViewParent);
- }
- super.updateMenuView(cleared);
-
- ((View) mMenuView).requestLayout();
-
- if (mMenu != null) {
- final ArrayList<MenuItemImpl> actionItems = mMenu.getActionItems();
- final int count = actionItems.size();
- for (int i = 0; i < count; i++) {
- final ActionProvider provider = actionItems.get(i).getActionProvider();
- if (provider != null) {
- provider.setSubUiVisibilityListener(this);
- }
- }
- }
-
- final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ?
- mMenu.getNonActionItems() : null;
-
- boolean hasOverflow = false;
- if (mReserveOverflow && nonActionItems != null) {
- final int count = nonActionItems.size();
- if (count == 1) {
- hasOverflow = !nonActionItems.get(0).isActionViewExpanded();
- } else {
- hasOverflow = count > 0;
- }
- }
-
- if (hasOverflow) {
- if (mOverflowButton == null) {
- mOverflowButton = new OverflowMenuButton(mSystemContext);
- }
- ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
- if (parent != mMenuView) {
- if (parent != null) {
- parent.removeView(mOverflowButton);
- }
- ActionMenuView menuView = (ActionMenuView) mMenuView;
- menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams());
- }
- } else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
- ((ViewGroup) mMenuView).removeView(mOverflowButton);
- }
-
- ((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow);
- }
-
- @Override
- public boolean filterLeftoverView(ViewGroup parent, int childIndex) {
- if (parent.getChildAt(childIndex) == mOverflowButton) return false;
- return super.filterLeftoverView(parent, childIndex);
- }
-
- public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
- if (!subMenu.hasVisibleItems()) return false;
-
- SubMenuBuilder topSubMenu = subMenu;
- while (topSubMenu.getParentMenu() != mMenu) {
- topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
- }
- View anchor = findViewForItem(topSubMenu.getItem());
- if (anchor == null) {
- if (mOverflowButton == null) return false;
- anchor = mOverflowButton;
- }
-
- mOpenSubMenuId = subMenu.getItem().getItemId();
- mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
- mActionButtonPopup.setAnchorView(anchor);
- mActionButtonPopup.show();
- super.onSubMenuSelected(subMenu);
- return true;
- }
-
- private View findViewForItem(MenuItem item) {
- final ViewGroup parent = (ViewGroup) mMenuView;
- if (parent == null) return null;
-
- final int count = parent.getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = parent.getChildAt(i);
- if (child instanceof MenuView.ItemView &&
- ((MenuView.ItemView) child).getItemData() == item) {
- return child;
- }
- }
- return null;
- }
-
- /**
- * Display the overflow menu if one is present.
- * @return true if the overflow menu was shown, false otherwise.
- */
- public boolean showOverflowMenu() {
- if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
- mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) {
- OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
- mPostedOpenRunnable = new OpenOverflowRunnable(popup);
- // Post this for later; we might still need a layout for the anchor to be right.
- ((View) mMenuView).post(mPostedOpenRunnable);
-
- // ActionMenuPresenter uses null as a callback argument here
- // to indicate overflow is opening.
- super.onSubMenuSelected(null);
-
- return true;
- }
- return false;
- }
-
- /**
- * Hide the overflow menu if it is currently showing.
- *
- * @return true if the overflow menu was hidden, false otherwise.
- */
- public boolean hideOverflowMenu() {
- if (mPostedOpenRunnable != null && mMenuView != null) {
- ((View) mMenuView).removeCallbacks(mPostedOpenRunnable);
- mPostedOpenRunnable = null;
- return true;
- }
-
- MenuPopupHelper popup = mOverflowPopup;
- if (popup != null) {
- popup.dismiss();
- return true;
- }
- return false;
- }
-
- /**
- * Dismiss all popup menus - overflow and submenus.
- * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
- */
- public boolean dismissPopupMenus() {
- boolean result = hideOverflowMenu();
- result |= hideSubMenus();
- return result;
- }
-
- /**
- * Dismiss all submenu popups.
- *
- * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
- */
- public boolean hideSubMenus() {
- if (mActionButtonPopup != null) {
- mActionButtonPopup.dismiss();
- return true;
- }
- return false;
- }
-
- /**
- * @return true if the overflow menu is currently showing
- */
- public boolean isOverflowMenuShowing() {
- return mOverflowPopup != null && mOverflowPopup.isShowing();
- }
-
- public boolean isOverflowMenuShowPending() {
- return mPostedOpenRunnable != null || isOverflowMenuShowing();
- }
-
- /**
- * @return true if space has been reserved in the action menu for an overflow item.
- */
- public boolean isOverflowReserved() {
- return mReserveOverflow;
- }
-
- public boolean flagActionItems() {
- final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
- final int itemsSize = visibleItems.size();
- int maxActions = mMaxItems;
- int widthLimit = mActionItemWidthLimit;
- final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- final ViewGroup parent = (ViewGroup) mMenuView;
-
- int requiredItems = 0;
- int requestedItems = 0;
- int firstActionWidth = 0;
- boolean hasOverflow = false;
- for (int i = 0; i < itemsSize; i++) {
- MenuItemImpl item = visibleItems.get(i);
- if (item.requiresActionButton()) {
- requiredItems++;
- } else if (item.requestsActionButton()) {
- requestedItems++;
- } else {
- hasOverflow = true;
- }
- if (mExpandedActionViewsExclusive && item.isActionViewExpanded()) {
- // Overflow everything if we have an expanded action view and we're
- // space constrained.
- maxActions = 0;
- }
- }
-
- // Reserve a spot for the overflow item if needed.
- if (mReserveOverflow &&
- (hasOverflow || requiredItems + requestedItems > maxActions)) {
- maxActions--;
- }
- maxActions -= requiredItems;
-
- final SparseBooleanArray seenGroups = mActionButtonGroups;
- seenGroups.clear();
-
- int cellSize = 0;
- int cellsRemaining = 0;
- if (mStrictWidthLimit) {
- cellsRemaining = widthLimit / mMinCellSize;
- final int cellSizeRemaining = widthLimit % mMinCellSize;
- cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining;
- }
-
- // Flag as many more requested items as will fit.
- for (int i = 0; i < itemsSize; i++) {
- MenuItemImpl item = visibleItems.get(i);
-
- if (item.requiresActionButton()) {
- View v = getItemView(item, mScrapActionButtonView, parent);
- if (mScrapActionButtonView == null) {
- mScrapActionButtonView = v;
- }
- if (mStrictWidthLimit) {
- cellsRemaining -= ActionMenuView.measureChildForCells(v,
- cellSize, cellsRemaining, querySpec, 0);
- } else {
- v.measure(querySpec, querySpec);
- }
- final int measuredWidth = v.getMeasuredWidth();
- widthLimit -= measuredWidth;
- if (firstActionWidth == 0) {
- firstActionWidth = measuredWidth;
- }
- final int groupId = item.getGroupId();
- if (groupId != 0) {
- seenGroups.put(groupId, true);
- }
- item.setIsActionButton(true);
- } else if (item.requestsActionButton()) {
- // Items in a group with other items that already have an action slot
- // can break the max actions rule, but not the width limit.
- final int groupId = item.getGroupId();
- final boolean inGroup = seenGroups.get(groupId);
- boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 &&
- (!mStrictWidthLimit || cellsRemaining > 0);
-
- if (isAction) {
- View v = getItemView(item, mScrapActionButtonView, parent);
- if (mScrapActionButtonView == null) {
- mScrapActionButtonView = v;
- }
- if (mStrictWidthLimit) {
- final int cells = ActionMenuView.measureChildForCells(v,
- cellSize, cellsRemaining, querySpec, 0);
- cellsRemaining -= cells;
- if (cells == 0) {
- isAction = false;
- }
- } else {
- v.measure(querySpec, querySpec);
- }
- final int measuredWidth = v.getMeasuredWidth();
- widthLimit -= measuredWidth;
- if (firstActionWidth == 0) {
- firstActionWidth = measuredWidth;
- }
-
- if (mStrictWidthLimit) {
- isAction &= widthLimit >= 0;
- } else {
- // Did this push the entire first item past the limit?
- isAction &= widthLimit + firstActionWidth > 0;
- }
- }
-
- if (isAction && groupId != 0) {
- seenGroups.put(groupId, true);
- } else if (inGroup) {
- // We broke the width limit. Demote the whole group, they all overflow now.
- seenGroups.put(groupId, false);
- for (int j = 0; j < i; j++) {
- MenuItemImpl areYouMyGroupie = visibleItems.get(j);
- if (areYouMyGroupie.getGroupId() == groupId) {
- // Give back the action slot
- if (areYouMyGroupie.isActionButton()) maxActions++;
- areYouMyGroupie.setIsActionButton(false);
- }
- }
- }
-
- if (isAction) maxActions--;
-
- item.setIsActionButton(isAction);
- } else {
- // Neither requires nor requests an action button.
- item.setIsActionButton(false);
- }
- }
- return true;
- }
-
- @Override
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- dismissPopupMenus();
- super.onCloseMenu(menu, allMenusAreClosing);
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- SavedState state = new SavedState();
- state.openSubMenuId = mOpenSubMenuId;
- return state;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- SavedState saved = (SavedState) state;
- if (saved.openSubMenuId > 0) {
- MenuItem item = mMenu.findItem(saved.openSubMenuId);
- if (item != null) {
- SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
- onSubMenuSelected(subMenu);
- }
- }
- }
-
- @Override
- public void onSubUiVisibilityChanged(boolean isVisible) {
- if (isVisible) {
- // Not a submenu, but treat it like one.
- super.onSubMenuSelected(null);
- } else {
- mMenu.close(false);
- }
- }
-
- private static class SavedState implements Parcelable {
- public int openSubMenuId;
-
- SavedState() {
- }
-
- SavedState(Parcel in) {
- openSubMenuId = in.readInt();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(openSubMenuId);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- private class OverflowMenuButton extends ImageButton implements ActionMenuChildView {
- public OverflowMenuButton(Context context) {
- super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
-
- setClickable(true);
- setFocusable(true);
- setVisibility(VISIBLE);
- setEnabled(true);
-
- setOnTouchListener(new ForwardingListener(this) {
- @Override
- public ListPopupWindow getPopup() {
- if (mOverflowPopup == null) {
- return null;
- }
-
- return mOverflowPopup.getPopup();
- }
-
- @Override
- public boolean onForwardingStarted() {
- showOverflowMenu();
- return true;
- }
-
- @Override
- public boolean onForwardingStopped() {
- // Displaying the popup occurs asynchronously, so wait for
- // the runnable to finish before deciding whether to stop
- // forwarding.
- if (mPostedOpenRunnable != null) {
- return false;
- }
-
- hideOverflowMenu();
- return true;
- }
- });
- }
-
- @Override
- public boolean performClick() {
- if (super.performClick()) {
- return true;
- }
-
- playSoundEffect(SoundEffectConstants.CLICK);
- showOverflowMenu();
- return true;
- }
-
- @Override
- public boolean needsDividerBefore() {
- return false;
- }
-
- @Override
- public boolean needsDividerAfter() {
- return false;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
- // Fill available height
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- info.setCanOpenPopup(true);
- }
- }
-
- private class OverflowPopup extends MenuPopupHelper {
- public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
- boolean overflowOnly) {
- super(context, menu, anchorView, overflowOnly);
- setGravity(Gravity.END);
- setCallback(mPopupPresenterCallback);
- }
-
- @Override
- public void onDismiss() {
- super.onDismiss();
- mMenu.close();
- mOverflowPopup = null;
- }
- }
-
- private class ActionButtonSubmenu extends MenuPopupHelper {
- private SubMenuBuilder mSubMenu;
-
- public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
- super(context, subMenu);
- mSubMenu = subMenu;
-
- MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
- if (!item.isActionButton()) {
- // Give a reasonable anchor to nested submenus.
- setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
- }
-
- 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
- public void onDismiss() {
- super.onDismiss();
- mActionButtonPopup = null;
- mOpenSubMenuId = 0;
- }
- }
-
- private class PopupPresenterCallback implements MenuPresenter.Callback {
-
- @Override
- public boolean onOpenSubMenu(MenuBuilder subMenu) {
- if (subMenu == null) return false;
-
- mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
- final MenuPresenter.Callback cb = getCallback();
- return cb != null ? cb.onOpenSubMenu(subMenu) : false;
- }
-
- @Override
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- if (menu instanceof SubMenuBuilder) {
- ((SubMenuBuilder) menu).getRootMenu().close(false);
- }
- final MenuPresenter.Callback cb = getCallback();
- if (cb != null) {
- cb.onCloseMenu(menu, allMenusAreClosing);
- }
- }
- }
-
- private class OpenOverflowRunnable implements Runnable {
- private OverflowPopup mPopup;
-
- public OpenOverflowRunnable(OverflowPopup popup) {
- mPopup = popup;
- }
-
- public void run() {
- mMenu.changeMenuMode();
- final View menuView = (View) mMenuView;
- if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
- mOverflowPopup = mPopup;
- }
- mPostedOpenRunnable = null;
- }
- }
-}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
deleted file mode 100644
index 16a2031..0000000
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ /dev/null
@@ -1,620 +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 com.android.internal.view.menu;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.LinearLayout;
-import com.android.internal.R;
-
-/**
- * @hide
- */
-public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvoker, MenuView {
- private static final String TAG = "ActionMenuView";
-
- static final int MIN_CELL_SIZE = 56; // dips
- static final int GENERATED_ITEM_PADDING = 4; // dips
-
- private MenuBuilder mMenu;
-
- private boolean mReserveOverflow;
- private ActionMenuPresenter mPresenter;
- private boolean mFormatItems;
- private int mFormatItemsWidth;
- private int mMinCellSize;
- private int mGeneratedItemPadding;
- private int mMeasuredExtraWidth;
- private int mMaxItemHeight;
-
- public ActionMenuView(Context context) {
- this(context, null);
- }
-
- public ActionMenuView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setBaselineAligned(false);
- final float density = context.getResources().getDisplayMetrics().density;
- mMinCellSize = (int) (MIN_CELL_SIZE * density);
- mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar,
- R.attr.actionBarStyle, 0);
- mMaxItemHeight = a.getDimensionPixelSize(R.styleable.ActionBar_height, 0);
- a.recycle();
- }
-
- public void setPresenter(ActionMenuPresenter presenter) {
- mPresenter = presenter;
- }
-
- public boolean isExpandedFormat() {
- return mFormatItems;
- }
-
- public void setMaxItemHeight(int maxItemHeight) {
- mMaxItemHeight = maxItemHeight;
- requestLayout();
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mPresenter.updateMenuView(false);
-
- if (mPresenter != null && mPresenter.isOverflowMenuShowing()) {
- mPresenter.hideOverflowMenu();
- mPresenter.showOverflowMenu();
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // If we've been given an exact size to match, apply special formatting during layout.
- final boolean wasFormatted = mFormatItems;
- mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
-
- if (wasFormatted != mFormatItems) {
- mFormatItemsWidth = 0; // Reset this when switching modes
- }
-
- // Special formatting can change whether items can fit as action buttons.
- // Kick the menu and update presenters when this changes.
- final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
- mFormatItemsWidth = widthSize;
- mMenu.onItemsChanged(true);
- }
-
- if (mFormatItems) {
- onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
- } else {
- // Previous measurement at exact format may have set margins - reset them.
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.leftMargin = lp.rightMargin = 0;
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
- // We already know the width mode is EXACTLY if we're here.
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
- final int widthPadding = getPaddingLeft() + getPaddingRight();
- final int heightPadding = getPaddingTop() + getPaddingBottom();
-
- final int itemHeightSpec = heightMode == MeasureSpec.EXACTLY
- ? MeasureSpec.makeMeasureSpec(heightSize - heightPadding, MeasureSpec.EXACTLY)
- : MeasureSpec.makeMeasureSpec(
- Math.min(mMaxItemHeight, heightSize - heightPadding), MeasureSpec.AT_MOST);
-
- widthSize -= widthPadding;
-
- // Divide the view into cells.
- final int cellCount = widthSize / mMinCellSize;
- final int cellSizeRemaining = widthSize % mMinCellSize;
-
- if (cellCount == 0) {
- // Give up, nothing fits.
- setMeasuredDimension(widthSize, 0);
- return;
- }
-
- final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
-
- int cellsRemaining = cellCount;
- int maxChildHeight = 0;
- int maxCellsUsed = 0;
- int expandableItemCount = 0;
- int visibleItemCount = 0;
- boolean hasOverflow = false;
-
- // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
- long smallestItemsAt = 0;
-
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) continue;
-
- final boolean isGeneratedItem = child instanceof ActionMenuItemView;
- visibleItemCount++;
-
- if (isGeneratedItem) {
- // Reset padding for generated menu item views; it may change below
- // and views are recycled.
- child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
- }
-
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.expanded = false;
- lp.extraPixels = 0;
- lp.cellsUsed = 0;
- lp.expandable = false;
- lp.leftMargin = 0;
- lp.rightMargin = 0;
- lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
-
- // Overflow always gets 1 cell. No more, no less.
- final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
-
- final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
- itemHeightSpec, heightPadding);
-
- maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
- if (lp.expandable) expandableItemCount++;
- if (lp.isOverflowButton) hasOverflow = true;
-
- cellsRemaining -= cellsUsed;
- maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
- if (cellsUsed == 1) smallestItemsAt |= (1 << i);
- }
-
- // When we have overflow and a single expanded (text) item, we want to try centering it
- // visually in the available space even though overflow consumes some of it.
- final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
-
- // Divide space for remaining cells if we have items that can expand.
- // Try distributing whole leftover cells to smaller items first.
-
- boolean needsExpansion = false;
- while (expandableItemCount > 0 && cellsRemaining > 0) {
- int minCells = Integer.MAX_VALUE;
- long minCellsAt = 0; // Bit locations are indices of relevant child views
- int minCellsItemCount = 0;
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- // Don't try to expand items that shouldn't.
- if (!lp.expandable) continue;
-
- // Mark indices of children that can receive an extra cell.
- if (lp.cellsUsed < minCells) {
- minCells = lp.cellsUsed;
- minCellsAt = 1 << i;
- minCellsItemCount = 1;
- } else if (lp.cellsUsed == minCells) {
- minCellsAt |= 1 << i;
- minCellsItemCount++;
- }
- }
-
- // Items that get expanded will always be in the set of smallest items when we're done.
- smallestItemsAt |= minCellsAt;
-
- if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
-
- // We have enough cells, all minimum size items will be incremented.
- minCells++;
-
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if ((minCellsAt & (1 << i)) == 0) {
- // If this item is already at our small item count, mark it for later.
- if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
- continue;
- }
-
- if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
- // Add padding to this item such that it centers.
- child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
- }
- lp.cellsUsed++;
- lp.expanded = true;
- cellsRemaining--;
- }
-
- needsExpansion = true;
- }
-
- // Divide any space left that wouldn't divide along cell boundaries
- // evenly among the smallest items
-
- final boolean singleItem = !hasOverflow && visibleItemCount == 1;
- if (cellsRemaining > 0 && smallestItemsAt != 0 &&
- (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
- float expandCount = Long.bitCount(smallestItemsAt);
-
- if (!singleItem) {
- // The items at the far edges may only expand by half in order to pin to either side.
- if ((smallestItemsAt & 1) != 0) {
- LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
- if (!lp.preventEdgeOffset) expandCount -= 0.5f;
- }
- if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
- LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
- if (!lp.preventEdgeOffset) expandCount -= 0.5f;
- }
- }
-
- final int extraPixels = expandCount > 0 ?
- (int) (cellsRemaining * cellSize / expandCount) : 0;
-
- for (int i = 0; i < childCount; i++) {
- if ((smallestItemsAt & (1 << i)) == 0) continue;
-
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (child instanceof ActionMenuItemView) {
- // If this is one of our views, expand and measure at the larger size.
- lp.extraPixels = extraPixels;
- lp.expanded = true;
- if (i == 0 && !lp.preventEdgeOffset) {
- // First item gets part of its new padding pushed out of sight.
- // The last item will get this implicitly from layout.
- lp.leftMargin = -extraPixels / 2;
- }
- needsExpansion = true;
- } else if (lp.isOverflowButton) {
- lp.extraPixels = extraPixels;
- lp.expanded = true;
- lp.rightMargin = -extraPixels / 2;
- needsExpansion = true;
- } else {
- // If we don't know what it is, give it some margins instead
- // and let it center within its space. We still want to pin
- // against the edges.
- if (i != 0) {
- lp.leftMargin = extraPixels / 2;
- }
- if (i != childCount - 1) {
- lp.rightMargin = extraPixels / 2;
- }
- }
- }
-
- cellsRemaining = 0;
- }
-
- // Remeasure any items that have had extra space allocated to them.
- if (needsExpansion) {
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if (!lp.expanded) continue;
-
- final int width = lp.cellsUsed * cellSize + lp.extraPixels;
- child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- itemHeightSpec);
- }
- }
-
- if (heightMode != MeasureSpec.EXACTLY) {
- heightSize = maxChildHeight;
- }
-
- setMeasuredDimension(widthSize, heightSize);
- mMeasuredExtraWidth = cellsRemaining * cellSize;
- }
-
- /**
- * Measure a child view to fit within cell-based formatting. The child's width
- * will be measured to a whole multiple of cellSize.
- *
- * <p>Sets the expandable and cellsUsed fields of LayoutParams.
- *
- * @param child Child to measure
- * @param cellSize Size of one cell
- * @param cellsRemaining Number of cells remaining that this view can expand to fill
- * @param parentHeightMeasureSpec MeasureSpec used by the parent view
- * @param parentHeightPadding Padding present in the parent view
- * @return Number of cells this child was measured to occupy
- */
- static int measureChildForCells(View child, int cellSize, int cellsRemaining,
- int parentHeightMeasureSpec, int parentHeightPadding) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
- parentHeightPadding;
- final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
- final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
-
- final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
- (ActionMenuItemView) child : null;
- final boolean hasText = itemView != null && itemView.hasText();
-
- int cellsUsed = 0;
- if (cellsRemaining > 0 && (!hasText || cellsRemaining >= 2)) {
- final int childWidthSpec = MeasureSpec.makeMeasureSpec(
- cellSize * cellsRemaining, MeasureSpec.AT_MOST);
- child.measure(childWidthSpec, childHeightSpec);
-
- final int measuredWidth = child.getMeasuredWidth();
- cellsUsed = measuredWidth / cellSize;
- if (measuredWidth % cellSize != 0) cellsUsed++;
- if (hasText && cellsUsed < 2) cellsUsed = 2;
- }
-
- final boolean expandable = !lp.isOverflowButton && hasText;
- lp.expandable = expandable;
-
- lp.cellsUsed = cellsUsed;
- final int targetWidth = cellsUsed * cellSize;
- child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
- childHeightSpec);
- return cellsUsed;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (!mFormatItems) {
- super.onLayout(changed, left, top, right, bottom);
- return;
- }
-
- final int childCount = getChildCount();
- final int midVertical = (top + bottom) / 2;
- final int dividerWidth = getDividerWidth();
- int overflowWidth = 0;
- int nonOverflowWidth = 0;
- int nonOverflowCount = 0;
- int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
- boolean hasOverflow = false;
- final boolean isLayoutRtl = isLayoutRtl();
- for (int i = 0; i < childCount; i++) {
- final View v = getChildAt(i);
- if (v.getVisibility() == GONE) {
- continue;
- }
-
- LayoutParams p = (LayoutParams) v.getLayoutParams();
- if (p.isOverflowButton) {
- overflowWidth = v.getMeasuredWidth();
- if (hasDividerBeforeChildAt(i)) {
- overflowWidth += dividerWidth;
- }
-
- int height = v.getMeasuredHeight();
- int r;
- int l;
- if (isLayoutRtl) {
- l = getPaddingLeft() + p.leftMargin;
- r = l + overflowWidth;
- } else {
- r = getWidth() - getPaddingRight() - p.rightMargin;
- l = r - overflowWidth;
- }
- int t = midVertical - (height / 2);
- int b = t + height;
- v.layout(l, t, r, b);
-
- widthRemaining -= overflowWidth;
- hasOverflow = true;
- } else {
- final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
- nonOverflowWidth += size;
- widthRemaining -= size;
- if (hasDividerBeforeChildAt(i)) {
- nonOverflowWidth += dividerWidth;
- }
- nonOverflowCount++;
- }
- }
-
- if (childCount == 1 && !hasOverflow) {
- // Center a single child
- final View v = getChildAt(0);
- final int width = v.getMeasuredWidth();
- final int height = v.getMeasuredHeight();
- final int midHorizontal = (right - left) / 2;
- final int l = midHorizontal - width / 2;
- final int t = midVertical - height / 2;
- v.layout(l, t, l + width, t + height);
- return;
- }
-
- final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
- final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
-
- if (isLayoutRtl) {
- int startRight = getWidth() - getPaddingRight();
- for (int i = 0; i < childCount; i++) {
- final View v = getChildAt(i);
- final LayoutParams lp = (LayoutParams) v.getLayoutParams();
- if (v.getVisibility() == GONE || lp.isOverflowButton) {
- continue;
- }
-
- startRight -= lp.rightMargin;
- int width = v.getMeasuredWidth();
- int height = v.getMeasuredHeight();
- int t = midVertical - height / 2;
- v.layout(startRight - width, t, startRight, t + height);
- startRight -= width + lp.leftMargin + spacerSize;
- }
- } else {
- int startLeft = getPaddingLeft();
- for (int i = 0; i < childCount; i++) {
- final View v = getChildAt(i);
- final LayoutParams lp = (LayoutParams) v.getLayoutParams();
- if (v.getVisibility() == GONE || lp.isOverflowButton) {
- continue;
- }
-
- startLeft += lp.leftMargin;
- int width = v.getMeasuredWidth();
- int height = v.getMeasuredHeight();
- int t = midVertical - height / 2;
- v.layout(startLeft, t, startLeft + width, t + height);
- startLeft += width + lp.rightMargin + spacerSize;
- }
- }
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mPresenter.dismissPopupMenus();
- }
-
- public boolean isOverflowReserved() {
- return mReserveOverflow;
- }
-
- public void setOverflowReserved(boolean reserveOverflow) {
- mReserveOverflow = reserveOverflow;
- }
-
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- params.gravity = Gravity.CENTER_VERTICAL;
- return params;
- }
-
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
-
- @Override
- protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- if (p != null) {
- final LayoutParams result = p instanceof LayoutParams
- ? new LayoutParams((LayoutParams) p)
- : new LayoutParams(p);
- if (result.gravity <= Gravity.NO_GRAVITY) {
- result.gravity = Gravity.CENTER_VERTICAL;
- }
- return result;
- }
- return generateDefaultLayoutParams();
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p != null && p instanceof LayoutParams;
- }
-
- public LayoutParams generateOverflowButtonLayoutParams() {
- LayoutParams result = generateDefaultLayoutParams();
- result.isOverflowButton = true;
- return result;
- }
-
- public boolean invokeItem(MenuItemImpl item) {
- return mMenu.performItemAction(item, 0);
- }
-
- public int getWindowAnimations() {
- return 0;
- }
-
- public void initialize(MenuBuilder menu) {
- mMenu = menu;
- }
-
- @Override
- protected boolean hasDividerBeforeChildAt(int childIndex) {
- if (childIndex == 0) {
- return false;
- }
- final View childBefore = getChildAt(childIndex - 1);
- final View child = getChildAt(childIndex);
- boolean result = false;
- if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
- result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
- }
- if (childIndex > 0 && child instanceof ActionMenuChildView) {
- result |= ((ActionMenuChildView) child).needsDividerBefore();
- }
- return result;
- }
-
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- return false;
- }
-
- public interface ActionMenuChildView {
- public boolean needsDividerBefore();
- public boolean needsDividerAfter();
- }
-
- public static class LayoutParams extends LinearLayout.LayoutParams {
- @ViewDebug.ExportedProperty(category = "layout")
- public boolean isOverflowButton;
- @ViewDebug.ExportedProperty(category = "layout")
- public int cellsUsed;
- @ViewDebug.ExportedProperty(category = "layout")
- public int extraPixels;
- @ViewDebug.ExportedProperty(category = "layout")
- public boolean expandable;
- @ViewDebug.ExportedProperty(category = "layout")
- public boolean preventEdgeOffset;
-
- public boolean expanded;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- }
-
- public LayoutParams(ViewGroup.LayoutParams other) {
- super(other);
- }
-
- public LayoutParams(LayoutParams other) {
- super((LinearLayout.LayoutParams) other);
- isOverflowButton = other.isOverflowButton;
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- isOverflowButton = false;
- }
-
- public LayoutParams(int width, int height, boolean isOverflowButton) {
- super(width, height);
- this.isOverflowButton = isOverflowButton;
- }
- }
-}
diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java
index 5d0b25f..de5e279 100644
--- a/core/java/com/android/internal/view/menu/IconMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java
@@ -57,8 +57,8 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie
private static String sPrependShortcutLabel;
- public IconMenuItemView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs);
+ public IconMenuItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
if (sPrependShortcutLabel == null) {
/*
@@ -68,10 +68,9 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie
sPrependShortcutLabel = getResources().getString(
com.android.internal.R.string.prepend_shortcut_label);
}
-
- TypedArray a =
- context.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.MenuView, defStyle, 0);
+
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, com.android.internal.R.styleable.MenuView, defStyleAttr, defStyleRes);
mDisabledAlpha = a.getFloat(
com.android.internal.R.styleable.MenuView_itemIconDisabledAlpha, 0.8f);
@@ -81,7 +80,11 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie
a.recycle();
}
-
+
+ public IconMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
public IconMenuItemView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index a2a4acc..692bdac 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -55,13 +55,13 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView
private boolean mForceShowIcon;
- public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs);
-
- TypedArray a =
- context.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.MenuView, defStyle, 0);
-
+ public ListMenuItemView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, com.android.internal.R.styleable.MenuView, defStyleAttr, defStyleRes);
+
mBackground = a.getDrawable(com.android.internal.R.styleable.MenuView_itemBackground);
mTextAppearance = a.getResourceId(com.android.internal.R.styleable.
MenuView_itemTextAppearance, -1);
@@ -72,6 +72,10 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView
a.recycle();
}
+ public ListMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
public ListMenuItemView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
index e1bb3621..c476354 100644
--- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -17,7 +17,6 @@
package com.android.internal.view.menu;
import android.content.Context;
-import android.database.DataSetObserver;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.SparseArray;
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 5464284..5d7d322 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -926,7 +926,7 @@ public class MenuBuilder implements Menu {
* sub menu is about to be shown, <var>allMenusAreClosing</var>
* is false.
*/
- final void close(boolean allMenusAreClosing) {
+ public final void close(boolean allMenusAreClosing) {
if (mIsClosing) return;
mIsClosing = true;
@@ -953,7 +953,7 @@ public class MenuBuilder implements Menu {
* false if only item properties changed.
* (Visibility is a structural property since it affects layout.)
*/
- void onItemsChanged(boolean structureChanged) {
+ public void onItemsChanged(boolean structureChanged) {
if (!mPreventDispatchingItemsChanged) {
if (structureChanged) {
mIsVisibleItemsStale = true;
@@ -1007,7 +1007,7 @@ public class MenuBuilder implements Menu {
onItemsChanged(true);
}
- ArrayList<MenuItemImpl> getVisibleItems() {
+ public ArrayList<MenuItemImpl> getVisibleItems() {
if (!mIsVisibleItemsStale) return mVisibleItems;
// Refresh the visible items
@@ -1092,12 +1092,12 @@ public class MenuBuilder implements Menu {
mIsActionItemsStale = false;
}
- ArrayList<MenuItemImpl> getActionItems() {
+ public ArrayList<MenuItemImpl> getActionItems() {
flagActionItems();
return mActionItems;
}
- ArrayList<MenuItemImpl> getNonActionItems() {
+ public ArrayList<MenuItemImpl> getNonActionItems() {
flagActionItems();
return mNonActionItems;
}
@@ -1128,7 +1128,7 @@ public class MenuBuilder implements Menu {
}
if (iconRes > 0) {
- mHeaderIcon = r.getDrawable(iconRes);
+ mHeaderIcon = getContext().getDrawable(iconRes);
} else if (icon != null) {
mHeaderIcon = icon;
}
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 4d0a326..61dcaca 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -385,7 +385,7 @@ public final class MenuItemImpl implements MenuItem {
}
if (mIconResId != NO_ICON) {
- Drawable icon = mMenu.getResources().getDrawable(mIconResId);
+ Drawable icon = mMenu.getContext().getDrawable(mIconResId);
mIconResId = NO_ICON;
mIconDrawable = icon;
return icon;
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 05e9a66..5a12893 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -23,7 +23,6 @@ import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
@@ -54,6 +53,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
private final MenuAdapter mAdapter;
private final boolean mOverflowOnly;
private final int mPopupMaxWidth;
+ private final int mPopupStyleAttr;
private View mAnchorView;
private ListPopupWindow mPopup;
@@ -73,20 +73,21 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
private int mDropDownGravity = Gravity.NO_GRAVITY;
public MenuPopupHelper(Context context, MenuBuilder menu) {
- this(context, menu, null, false);
+ this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle);
}
public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) {
- this(context, menu, anchorView, false);
+ this(context, menu, anchorView, false, com.android.internal.R.attr.popupMenuStyle);
}
- public MenuPopupHelper(Context context, MenuBuilder menu,
- View anchorView, boolean overflowOnly) {
+ public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView,
+ boolean overflowOnly, int popupStyleAttr) {
mContext = context;
mInflater = LayoutInflater.from(context);
mMenu = menu;
mAdapter = new MenuAdapter(mMenu);
mOverflowOnly = overflowOnly;
+ mPopupStyleAttr = popupStyleAttr;
final Resources res = context.getResources();
mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
@@ -120,7 +121,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
}
public boolean tryShow() {
- mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle);
+ mPopup = new ListPopupWindow(mContext, null, mPopupStyleAttr);
mPopup.setOnDismissListener(this);
mPopup.setOnItemClickListener(this);
mPopup.setAdapter(mAdapter);
@@ -273,7 +274,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
@Override
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
if (subMenu.hasVisibleItems()) {
- MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView, false);
+ MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView);
subPopup.setCallback(mPresenterCallback);
boolean preserveIconSpacing = false;
diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java
index f3891c7..9e7ff93 100644
--- a/core/java/com/android/internal/widget/AbsActionBarView.java
+++ b/core/java/com/android/internal/widget/AbsActionBarView.java
@@ -16,8 +16,8 @@
package com.android.internal.widget;
import com.android.internal.R;
-import com.android.internal.view.menu.ActionMenuPresenter;
-import com.android.internal.view.menu.ActionMenuView;
+import android.widget.ActionMenuPresenter;
+import android.widget.ActionMenuView;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -34,7 +34,7 @@ import android.view.animation.DecelerateInterpolator;
public abstract class AbsActionBarView extends ViewGroup {
protected ActionMenuView mMenuView;
protected ActionMenuPresenter mActionMenuPresenter;
- protected ActionBarContainer mSplitView;
+ protected ViewGroup mSplitView;
protected boolean mSplitActionBar;
protected boolean mSplitWhenNarrow;
protected int mContentHeight;
@@ -47,15 +47,20 @@ public abstract class AbsActionBarView extends ViewGroup {
private static final int FADE_DURATION = 200;
public AbsActionBarView(Context context) {
- super(context);
+ this(context, null);
}
public AbsActionBarView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
- public AbsActionBarView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public AbsActionBarView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
@@ -69,7 +74,7 @@ public abstract class AbsActionBarView extends ViewGroup {
setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0));
a.recycle();
if (mSplitWhenNarrow) {
- setSplitActionBar(getContext().getResources().getBoolean(
+ setSplitToolbar(getContext().getResources().getBoolean(
com.android.internal.R.bool.split_action_bar_is_narrow));
}
if (mActionMenuPresenter != null) {
@@ -81,7 +86,7 @@ public abstract class AbsActionBarView extends ViewGroup {
* Sets whether the bar should be split right now, no questions asked.
* @param split true if the bar should split
*/
- public void setSplitActionBar(boolean split) {
+ public void setSplitToolbar(boolean split) {
mSplitActionBar = split;
}
@@ -95,9 +100,6 @@ public abstract class AbsActionBarView extends ViewGroup {
public void setContentHeight(int height) {
mContentHeight = height;
- if (mMenuView != null) {
- mMenuView.setMaxItemHeight(mContentHeight);
- }
requestLayout();
}
@@ -105,7 +107,7 @@ public abstract class AbsActionBarView extends ViewGroup {
return mContentHeight;
}
- public void setSplitView(ActionBarContainer splitView) {
+ public void setSplitView(ViewGroup splitView) {
mSplitView = splitView;
}
@@ -212,6 +214,10 @@ public abstract class AbsActionBarView extends ViewGroup {
return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
}
+ public boolean canShowOverflowMenu() {
+ return isOverflowReserved() && getVisibility() == VISIBLE;
+ }
+
public void dismissPopupMenus() {
if (mActionMenuPresenter != null) {
mActionMenuPresenter.dismissPopupMenus();
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index 8a49899..790b611 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -16,10 +16,10 @@
package com.android.internal.widget;
-import android.app.ActionBar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.ColorFilter;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ActionMode;
@@ -36,13 +36,14 @@ import android.widget.FrameLayout;
public class ActionBarContainer extends FrameLayout {
private boolean mIsTransitioning;
private View mTabContainer;
- private ActionBarView mActionBarView;
+ private View mActionBarView;
private Drawable mBackground;
private Drawable mStackedBackground;
private Drawable mSplitBackground;
private boolean mIsSplit;
private boolean mIsStacked;
+ private int mHeight;
public ActionBarContainer(Context context) {
this(context, null);
@@ -51,13 +52,15 @@ public class ActionBarContainer extends FrameLayout {
public ActionBarContainer(Context context, AttributeSet attrs) {
super(context, attrs);
- setBackgroundDrawable(null);
+ // Set a transparent background so that we project appropriately.
+ setBackground(new ActionBarBackgroundDrawable());
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.ActionBar);
mBackground = a.getDrawable(com.android.internal.R.styleable.ActionBar_background);
mStackedBackground = a.getDrawable(
com.android.internal.R.styleable.ActionBar_backgroundStacked);
+ mHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.ActionBar_height, -1);
if (getId() == com.android.internal.R.id.split_action_bar) {
mIsSplit = true;
@@ -73,7 +76,7 @@ public class ActionBarContainer extends FrameLayout {
@Override
public void onFinishInflate() {
super.onFinishInflate();
- mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
+ mActionBarView = findViewById(com.android.internal.R.id.action_bar);
}
public void setPrimaryBackground(Drawable bg) {
@@ -243,37 +246,28 @@ public class ActionBarContainer extends FrameLayout {
}
@Override
- public void onDraw(Canvas canvas) {
- if (getWidth() == 0 || getHeight() == 0) {
- return;
- }
-
- if (mIsSplit) {
- if (mSplitBackground != null) mSplitBackground.draw(canvas);
- } else {
- if (mBackground != null) {
- mBackground.draw(canvas);
- }
- if (mStackedBackground != null && mIsStacked) {
- mStackedBackground.draw(canvas);
- }
- }
- }
-
- @Override
public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
// No starting an action mode for an action bar child! (Where would it go?)
return null;
}
+ private boolean isCollapsed(View view) {
+ return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
+ }
+
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mActionBarView == null &&
+ MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST);
+ }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mActionBarView == null) return;
final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams();
- final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 :
+ final int actionBarViewHeight = isCollapsed(mActionBarView) ? 0 :
mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
@@ -291,12 +285,13 @@ public class ActionBarContainer extends FrameLayout {
public void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- final boolean hasTabs = mTabContainer != null && mTabContainer.getVisibility() != GONE;
+ final View tabContainer = mTabContainer;
+ final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE;
- if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
+ if (tabContainer != null && tabContainer.getVisibility() != GONE) {
final int containerHeight = getMeasuredHeight();
- final int tabHeight = mTabContainer.getMeasuredHeight();
- mTabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
+ final int tabHeight = tabContainer.getMeasuredHeight();
+ tabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
}
boolean needsInvalidate = false;
@@ -311,9 +306,10 @@ public class ActionBarContainer extends FrameLayout {
mActionBarView.getRight(), mActionBarView.getBottom());
needsInvalidate = true;
}
- if ((mIsStacked = hasTabs && mStackedBackground != null)) {
- mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(),
- mTabContainer.getRight(), mTabContainer.getBottom());
+ mIsStacked = hasTabs;
+ if (hasTabs && mStackedBackground != null) {
+ mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(),
+ tabContainer.getRight(), tabContainer.getBottom());
needsInvalidate = true;
}
}
@@ -322,4 +318,37 @@ public class ActionBarContainer extends FrameLayout {
invalidate();
}
}
+
+ /**
+ * Dummy drawable so that we don't break background display lists and
+ * projection surfaces.
+ */
+ private class ActionBarBackgroundDrawable extends Drawable {
+ @Override
+ public void draw(Canvas canvas) {
+ if (mIsSplit) {
+ if (mSplitBackground != null) mSplitBackground.draw(canvas);
+ } else {
+ if (mBackground != null) {
+ mBackground.draw(canvas);
+ }
+ if (mStackedBackground != null && mIsStacked) {
+ mStackedBackground.draw(canvas);
+ }
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 8bc1081..6ff77a0 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -16,8 +16,8 @@
package com.android.internal.widget;
import com.android.internal.R;
-import com.android.internal.view.menu.ActionMenuPresenter;
-import com.android.internal.view.menu.ActionMenuView;
+import android.widget.ActionMenuPresenter;
+import android.widget.ActionMenuView;
import com.android.internal.view.menu.MenuBuilder;
import android.animation.Animator;
@@ -25,7 +25,6 @@ import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
@@ -74,11 +73,17 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
this(context, attrs, com.android.internal.R.attr.actionModeStyle);
}
- public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionMode, defStyle, 0);
- setBackgroundDrawable(a.getDrawable(
+ public ActionBarContextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public ActionBarContextView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.ActionMode, defStyleAttr, defStyleRes);
+ setBackground(a.getDrawable(
com.android.internal.R.styleable.ActionMode_background));
mTitleStyleRes = a.getResourceId(
com.android.internal.R.styleable.ActionMode_titleTextStyle, 0);
@@ -104,7 +109,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
}
@Override
- public void setSplitActionBar(boolean split) {
+ public void setSplitToolbar(boolean split) {
if (mSplitActionBar != split) {
if (mActionMenuPresenter != null) {
// Mode is already active; move everything over and adjust the menu itself.
@@ -132,7 +137,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
mSplitView.addView(mMenuView, layoutParams);
}
}
- super.setSplitActionBar(split);
+ super.setSplitToolbar(split);
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index c957b67..8a9cb22 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -16,44 +16,61 @@
package com.android.internal.widget;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-import com.android.internal.app.ActionBarImpl;
-
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.res.TypedArray;
+import android.graphics.Canvas;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.IntProperty;
+import android.util.Log;
+import android.util.Property;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+import android.view.Menu;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.widget.OverScroller;
+import android.widget.Toolbar;
+import com.android.internal.view.menu.MenuPresenter;
/**
* Special layout for the containing of an overlay action bar (and its
* content) to correctly handle fitting system windows when the content
* has request that its layout ignore them.
*/
-public class ActionBarOverlayLayout extends ViewGroup {
+public class ActionBarOverlayLayout extends ViewGroup implements DecorContentParent {
private static final String TAG = "ActionBarOverlayLayout";
private int mActionBarHeight;
- private ActionBarImpl mActionBar;
+ //private WindowDecorActionBar mActionBar;
private int mWindowVisibility = View.VISIBLE;
// The main UI elements that we handle the layout of.
private View mContent;
- private View mActionBarBottom;
+ private ActionBarContainer mActionBarBottom;
private ActionBarContainer mActionBarTop;
// Some interior UI elements.
- private ActionBarView mActionBarView;
+ private DecorToolbar mDecorToolbar;
// Content overlay drawable - generally the action bar's shadow
private Drawable mWindowContentOverlay;
private boolean mIgnoreWindowContentOverlay;
private boolean mOverlayMode;
+ private boolean mHasNonEmbeddedTabs;
+ private boolean mHideOnContentScroll;
+ private boolean mAnimatingForFling;
+ private int mHideOnContentScrollReference;
private int mLastSystemUiVisibility;
private final Rect mBaseContentInsets = new Rect();
private final Rect mLastBaseContentInsets = new Rect();
@@ -62,6 +79,84 @@ public class ActionBarOverlayLayout extends ViewGroup {
private final Rect mInnerInsets = new Rect();
private final Rect mLastInnerInsets = new Rect();
+ private ActionBarVisibilityCallback mActionBarVisibilityCallback;
+
+ private final int ACTION_BAR_ANIMATE_DELAY = 600; // ms
+
+ private OverScroller mFlingEstimator;
+
+ private ViewPropertyAnimator mCurrentActionBarTopAnimator;
+ private ViewPropertyAnimator mCurrentActionBarBottomAnimator;
+
+ private final Animator.AnimatorListener mTopAnimatorListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentActionBarTopAnimator = null;
+ mAnimatingForFling = false;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCurrentActionBarTopAnimator = null;
+ mAnimatingForFling = false;
+ }
+ };
+
+ private final Animator.AnimatorListener mBottomAnimatorListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentActionBarBottomAnimator = null;
+ mAnimatingForFling = false;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCurrentActionBarBottomAnimator = null;
+ mAnimatingForFling = false;
+ }
+ };
+
+ private final Runnable mRemoveActionBarHideOffset = new Runnable() {
+ public void run() {
+ haltActionBarHideOffsetAnimations();
+ mCurrentActionBarTopAnimator = mActionBarTop.animate().translationY(0)
+ .setListener(mTopAnimatorListener);
+ if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) {
+ mCurrentActionBarBottomAnimator = mActionBarBottom.animate().translationY(0)
+ .setListener(mBottomAnimatorListener);
+ }
+ }
+ };
+
+ private final Runnable mAddActionBarHideOffset = new Runnable() {
+ public void run() {
+ haltActionBarHideOffsetAnimations();
+ mCurrentActionBarTopAnimator = mActionBarTop.animate()
+ .translationY(-mActionBarTop.getHeight())
+ .setListener(mTopAnimatorListener);
+ if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) {
+ mCurrentActionBarBottomAnimator = mActionBarBottom.animate()
+ .translationY(mActionBarBottom.getHeight())
+ .setListener(mBottomAnimatorListener);
+ }
+ }
+ };
+
+ public static final Property<ActionBarOverlayLayout, Integer> ACTION_BAR_HIDE_OFFSET =
+ new IntProperty<ActionBarOverlayLayout>("actionBarHideOffset") {
+
+ @Override
+ public void setValue(ActionBarOverlayLayout object, int value) {
+ object.setActionBarHideOffset(value);
+ }
+
+ @Override
+ public Integer get(ActionBarOverlayLayout object) {
+ return object.getActionBarHideOffset();
+ }
+ };
+
static final int[] ATTRS = new int [] {
com.android.internal.R.attr.actionBarSize,
com.android.internal.R.attr.windowContentOverlay
@@ -86,14 +181,22 @@ public class ActionBarOverlayLayout extends ViewGroup {
mIgnoreWindowContentOverlay = context.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT;
+
+ mFlingEstimator = new OverScroller(context);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ haltActionBarHideOffsetAnimations();
}
- public void setActionBar(ActionBarImpl impl) {
- mActionBar = impl;
+ public void setActionBarVisibilityCallback(ActionBarVisibilityCallback cb) {
+ mActionBarVisibilityCallback = cb;
if (getWindowToken() != null) {
// This is being initialized after being added to a window;
// make sure to update all state now.
- mActionBar.setWindowVisibility(mWindowVisibility);
+ mActionBarVisibilityCallback.onWindowVisibilityChanged(mWindowVisibility);
if (mLastSystemUiVisibility != 0) {
int newVis = mLastSystemUiVisibility;
onWindowSystemUiVisibilityChanged(newVis);
@@ -114,6 +217,14 @@ public class ActionBarOverlayLayout extends ViewGroup {
Build.VERSION_CODES.KITKAT;
}
+ public boolean isInOverlayMode() {
+ return mOverlayMode;
+ }
+
+ public void setHasNonEmbeddedTabs(boolean hasNonEmbeddedTabs) {
+ mHasNonEmbeddedTabs = hasNonEmbeddedTabs;
+ }
+
public void setShowingForActionMode(boolean showing) {
if (showing) {
// Here's a fun hack: if the status bar is currently being hidden,
@@ -140,19 +251,18 @@ public class ActionBarOverlayLayout extends ViewGroup {
pullChildren();
final int diff = mLastSystemUiVisibility ^ visible;
mLastSystemUiVisibility = visible;
- final boolean barVisible = (visible&SYSTEM_UI_FLAG_FULLSCREEN) == 0;
- final boolean wasVisible = mActionBar != null ? mActionBar.isSystemShowing() : true;
- final boolean stable = (visible&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
- if (mActionBar != null) {
+ final boolean barVisible = (visible & SYSTEM_UI_FLAG_FULLSCREEN) == 0;
+ final boolean stable = (visible & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
+ if (mActionBarVisibilityCallback != null) {
// We want the bar to be visible if it is not being hidden,
// or the app has not turned on a stable UI mode (meaning they
// are performing explicit layout around the action bar).
- mActionBar.enableContentAnimations(!stable);
- if (barVisible || !stable) mActionBar.showForSystem();
- else mActionBar.hideForSystem();
+ mActionBarVisibilityCallback.enableContentAnimations(!stable);
+ if (barVisible || !stable) mActionBarVisibilityCallback.showForSystem();
+ else mActionBarVisibilityCallback.hideForSystem();
}
- if ((diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
- if (mActionBar != null) {
+ if ((diff & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+ if (mActionBarVisibilityCallback != null) {
requestApplyInsets();
}
}
@@ -162,8 +272,8 @@ public class ActionBarOverlayLayout extends ViewGroup {
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mWindowVisibility = visibility;
- if (mActionBar != null) {
- mActionBar.setWindowVisibility(visibility);
+ if (mActionBarVisibilityCallback != null) {
+ mActionBarVisibilityCallback.onWindowVisibilityChanged(visibility);
}
}
@@ -279,20 +389,20 @@ public class ActionBarOverlayLayout extends ViewGroup {
// This is the standard space needed for the action bar. For stable measurement,
// we can't depend on the size currently reported by it -- this must remain constant.
topInset = mActionBarHeight;
- if (mActionBar != null && mActionBar.hasNonEmbeddedTabs()) {
- View tabs = mActionBarTop.getTabContainer();
+ if (mHasNonEmbeddedTabs) {
+ final View tabs = mActionBarTop.getTabContainer();
if (tabs != null) {
// If tabs are not embedded, increase space on top to account for them.
topInset += mActionBarHeight;
}
}
- } else if (mActionBarTop.getVisibility() == VISIBLE) {
+ } else if (mActionBarTop.getVisibility() != GONE) {
// This is the space needed on top of the window for all of the action bar
// and tabs.
topInset = mActionBarTop.getMeasuredHeight();
}
- if (mActionBarView.isSplitActionBar()) {
+ if (mDecorToolbar.isSplit()) {
// If action bar is split, adjust bottom insets for it.
if (mActionBarBottom != null) {
if (stable) {
@@ -395,16 +505,323 @@ public class ActionBarOverlayLayout extends ViewGroup {
return false;
}
+ @Override
+ public boolean onStartNestedScroll(View child, View target, int axes) {
+ if ((axes & SCROLL_AXIS_VERTICAL) == 0 || mActionBarTop.getVisibility() != VISIBLE) {
+ return false;
+ }
+ return mHideOnContentScroll;
+ }
+
+ @Override
+ public void onNestedScrollAccepted(View child, View target, int axes) {
+ super.onNestedScrollAccepted(child, target, axes);
+ mHideOnContentScrollReference = getActionBarHideOffset();
+ haltActionBarHideOffsetAnimations();
+ if (mActionBarVisibilityCallback != null) {
+ mActionBarVisibilityCallback.onContentScrollStarted();
+ }
+ }
+
+ @Override
+ public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+ int dxUnconsumed, int dyUnconsumed) {
+ mHideOnContentScrollReference += dyConsumed;
+ setActionBarHideOffset(mHideOnContentScrollReference);
+ }
+
+ @Override
+ public void onStopNestedScroll(View target) {
+ super.onStopNestedScroll(target);
+ if (mHideOnContentScroll && !mAnimatingForFling) {
+ if (mHideOnContentScrollReference <= mActionBarTop.getHeight()) {
+ postRemoveActionBarHideOffset();
+ } else {
+ postAddActionBarHideOffset();
+ }
+ }
+ if (mActionBarVisibilityCallback != null) {
+ mActionBarVisibilityCallback.onContentScrollStopped();
+ }
+ }
+
+ @Override
+ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+ if (!mHideOnContentScroll || !consumed) {
+ return false;
+ }
+ if (shouldHideActionBarOnFling(velocityX, velocityY)) {
+ addActionBarHideOffset();
+ } else {
+ removeActionBarHideOffset();
+ }
+ mAnimatingForFling = true;
+ return true;
+ }
+
void pullChildren() {
if (mContent == null) {
mContent = findViewById(com.android.internal.R.id.content);
- mActionBarTop = (ActionBarContainer)findViewById(
+ mActionBarTop = (ActionBarContainer) findViewById(
com.android.internal.R.id.action_bar_container);
- mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
- mActionBarBottom = findViewById(com.android.internal.R.id.split_action_bar);
+ mDecorToolbar = getDecorToolbar(findViewById(com.android.internal.R.id.action_bar));
+ mActionBarBottom = (ActionBarContainer) findViewById(
+ com.android.internal.R.id.split_action_bar);
+ }
+ }
+
+ private DecorToolbar getDecorToolbar(View view) {
+ if (view instanceof DecorToolbar) {
+ return (DecorToolbar) view;
+ } else if (view instanceof Toolbar) {
+ return ((Toolbar) view).getWrapper();
+ } else {
+ throw new IllegalStateException("Can't make a decor toolbar out of " +
+ view.getClass().getSimpleName());
}
}
+ public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
+ if (hideOnContentScroll != mHideOnContentScroll) {
+ mHideOnContentScroll = hideOnContentScroll;
+ if (!hideOnContentScroll) {
+ stopNestedScroll();
+ haltActionBarHideOffsetAnimations();
+ setActionBarHideOffset(0);
+ }
+ }
+ }
+
+ public boolean isHideOnContentScrollEnabled() {
+ return mHideOnContentScroll;
+ }
+
+ public int getActionBarHideOffset() {
+ return mActionBarTop != null ? -((int) mActionBarTop.getTranslationY()) : 0;
+ }
+
+ public void setActionBarHideOffset(int offset) {
+ haltActionBarHideOffsetAnimations();
+ final int topHeight = mActionBarTop.getHeight();
+ offset = Math.max(0, Math.min(offset, topHeight));
+ mActionBarTop.setTranslationY(-offset);
+ if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) {
+ // Match the hide offset proportionally for a split bar
+ final float fOffset = (float) offset / topHeight;
+ final int bOffset = (int) (mActionBarBottom.getHeight() * fOffset);
+ mActionBarBottom.setTranslationY(bOffset);
+ }
+ }
+
+ private void haltActionBarHideOffsetAnimations() {
+ removeCallbacks(mRemoveActionBarHideOffset);
+ removeCallbacks(mAddActionBarHideOffset);
+ if (mCurrentActionBarTopAnimator != null) {
+ mCurrentActionBarTopAnimator.cancel();
+ }
+ if (mCurrentActionBarBottomAnimator != null) {
+ mCurrentActionBarBottomAnimator.cancel();
+ }
+ }
+
+ private void postRemoveActionBarHideOffset() {
+ haltActionBarHideOffsetAnimations();
+ postDelayed(mRemoveActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
+ }
+
+ private void postAddActionBarHideOffset() {
+ haltActionBarHideOffsetAnimations();
+ postDelayed(mAddActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
+ }
+
+ private void removeActionBarHideOffset() {
+ haltActionBarHideOffsetAnimations();
+ mRemoveActionBarHideOffset.run();
+ }
+
+ private void addActionBarHideOffset() {
+ haltActionBarHideOffsetAnimations();
+ mAddActionBarHideOffset.run();
+ }
+
+ private boolean shouldHideActionBarOnFling(float velocityX, float velocityY) {
+ mFlingEstimator.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
+ final int finalY = mFlingEstimator.getFinalY();
+ return finalY > mActionBarTop.getHeight();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (super.dispatchKeyEvent(event)) {
+ return true;
+ }
+
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ final int action = event.getAction();
+
+ // Collapse any expanded action views.
+ if (mDecorToolbar != null && mDecorToolbar.hasExpandedActionView()) {
+ if (action == KeyEvent.ACTION_UP) {
+ mDecorToolbar.collapseActionView();
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void setWindowCallback(Window.Callback cb) {
+ pullChildren();
+ mDecorToolbar.setWindowCallback(cb);
+ }
+
+ @Override
+ public void setWindowTitle(CharSequence title) {
+ pullChildren();
+ mDecorToolbar.setWindowTitle(title);
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ pullChildren();
+ return mDecorToolbar.getTitle();
+ }
+
+ @Override
+ public void initFeature(int windowFeature) {
+ pullChildren();
+ switch (windowFeature) {
+ case Window.FEATURE_PROGRESS:
+ mDecorToolbar.initProgress();
+ break;
+ case Window.FEATURE_INDETERMINATE_PROGRESS:
+ mDecorToolbar.initIndeterminateProgress();
+ break;
+ case Window.FEATURE_ACTION_BAR_OVERLAY:
+ setOverlayMode(true);
+ break;
+ }
+ }
+
+ @Override
+ public void setUiOptions(int uiOptions) {
+ boolean splitActionBar = false;
+ final boolean splitWhenNarrow =
+ (uiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
+ if (splitWhenNarrow) {
+ splitActionBar = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.split_action_bar_is_narrow);
+ }
+ if (splitActionBar) {
+ pullChildren();
+ if (mActionBarBottom != null && mDecorToolbar.canSplit()) {
+ mDecorToolbar.setSplitView(mActionBarBottom);
+ mDecorToolbar.setSplitToolbar(splitActionBar);
+ mDecorToolbar.setSplitWhenNarrow(splitWhenNarrow);
+
+ final ActionBarContextView cab = (ActionBarContextView) findViewById(
+ com.android.internal.R.id.action_context_bar);
+ cab.setSplitView(mActionBarBottom);
+ cab.setSplitToolbar(splitActionBar);
+ cab.setSplitWhenNarrow(splitWhenNarrow);
+ } else if (splitActionBar) {
+ Log.e(TAG, "Requested split action bar with " +
+ "incompatible window decor! Ignoring request.");
+ }
+ }
+ }
+
+ @Override
+ public boolean hasIcon() {
+ pullChildren();
+ return mDecorToolbar.hasIcon();
+ }
+
+ @Override
+ public boolean hasLogo() {
+ pullChildren();
+ return mDecorToolbar.hasLogo();
+ }
+
+ @Override
+ public void setIcon(int resId) {
+ pullChildren();
+ mDecorToolbar.setIcon(resId);
+ }
+
+ @Override
+ public void setIcon(Drawable d) {
+ pullChildren();
+ mDecorToolbar.setIcon(d);
+ }
+
+ @Override
+ public void setLogo(int resId) {
+ pullChildren();
+ mDecorToolbar.setLogo(resId);
+ }
+
+ @Override
+ public boolean canShowOverflowMenu() {
+ pullChildren();
+ return mDecorToolbar.canShowOverflowMenu();
+ }
+
+ @Override
+ public boolean isOverflowMenuShowing() {
+ pullChildren();
+ return mDecorToolbar.isOverflowMenuShowing();
+ }
+
+ @Override
+ public boolean isOverflowMenuShowPending() {
+ pullChildren();
+ return mDecorToolbar.isOverflowMenuShowPending();
+ }
+
+ @Override
+ public boolean showOverflowMenu() {
+ pullChildren();
+ return mDecorToolbar.showOverflowMenu();
+ }
+
+ @Override
+ public boolean hideOverflowMenu() {
+ pullChildren();
+ return mDecorToolbar.hideOverflowMenu();
+ }
+
+ @Override
+ public void setMenuPrepared() {
+ pullChildren();
+ mDecorToolbar.setMenuPrepared();
+ }
+
+ @Override
+ public void setMenu(Menu menu, MenuPresenter.Callback cb) {
+ pullChildren();
+ mDecorToolbar.setMenu(menu, cb);
+ }
+
+ @Override
+ public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
+ pullChildren();
+ mDecorToolbar.saveHierarchyState(toolbarStates);
+ }
+
+ @Override
+ public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
+ pullChildren();
+ mDecorToolbar.restoreHierarchyState(toolbarStates);
+ }
+
+ @Override
+ public void dismissPopups() {
+ pullChildren();
+ mDecorToolbar.dismissPopupMenus();
+ }
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
@@ -423,4 +840,13 @@ public class ActionBarOverlayLayout extends ViewGroup {
super(source);
}
}
+
+ public interface ActionBarVisibilityCallback {
+ void onWindowVisibilityChanged(int visibility);
+ void showForSystem();
+ void hideForSystem();
+ void enableContentAnimations(boolean enable);
+ void onContentScrollStarted();
+ void onContentScrollStopped();
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 786f5cf..af82778 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -18,7 +18,6 @@ package com.android.internal.widget;
import android.animation.LayoutTransition;
import android.app.ActionBar;
-import android.app.ActionBar.OnNavigationListener;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -41,6 +40,8 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.Window;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.ActionMenuPresenter;
+import android.widget.ActionMenuView;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -52,8 +53,6 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.transition.ActionBarTransition;
import com.android.internal.view.menu.ActionMenuItem;
-import com.android.internal.view.menu.ActionMenuPresenter;
-import com.android.internal.view.menu.ActionMenuView;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuItemImpl;
import com.android.internal.view.menu.MenuPresenter;
@@ -63,7 +62,7 @@ import com.android.internal.view.menu.SubMenuBuilder;
/**
* @hide
*/
-public class ActionBarView extends AbsActionBarView {
+public class ActionBarView extends AbsActionBarView implements DecorToolbar {
private static final String TAG = "ActionBarView";
/**
@@ -117,8 +116,7 @@ public class ActionBarView extends AbsActionBarView {
private boolean mUserTitle;
private boolean mIncludeTabs;
- private boolean mIsCollapsable;
- private boolean mIsCollapsed;
+ private boolean mIsCollapsible;
private boolean mWasHomeEnabled; // Was it enabled before action view expansion?
private MenuBuilder mOptionsMenu;
@@ -129,7 +127,7 @@ public class ActionBarView extends AbsActionBarView {
private ActionMenuItem mLogoNavItem;
private SpinnerAdapter mSpinnerAdapter;
- private OnNavigationListener mCallback;
+ private AdapterView.OnItemSelectedListener mNavItemSelectedListener;
private Runnable mTabSelector;
@@ -138,18 +136,6 @@ public class ActionBarView extends AbsActionBarView {
Window.Callback mWindowCallback;
- private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
- new AdapterView.OnItemSelectedListener() {
- public void onItemSelected(AdapterView parent, View view, int position, long id) {
- if (mCallback != null) {
- mCallback.onNavigationItemSelected(position, id);
- }
- }
- public void onNothingSelected(AdapterView parent) {
- // Do nothing
- }
- };
-
private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() {
@Override
public void onClick(View v) {
@@ -178,8 +164,6 @@ public class ActionBarView extends AbsActionBarView {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar,
com.android.internal.R.attr.actionBarStyle, 0);
- ApplicationInfo appInfo = context.getApplicationInfo();
- PackageManager pm = context.getPackageManager();
mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
ActionBar.NAVIGATION_MODE_STANDARD);
mTitle = a.getText(R.styleable.ActionBar_title);
@@ -260,7 +244,7 @@ public class ActionBarView extends AbsActionBarView {
}
if (mHomeDescriptionRes != 0) {
- setHomeActionContentDescription(mHomeDescriptionRes);
+ setNavigationContentDescription(mHomeDescriptionRes);
}
if (mTabScrollView != null && mIncludeTabs) {
@@ -313,7 +297,7 @@ public class ActionBarView extends AbsActionBarView {
}
@Override
- public void setSplitActionBar(boolean splitActionBar) {
+ public void setSplitToolbar(boolean splitActionBar) {
if (mSplitActionBar != splitActionBar) {
if (mMenuView != null) {
final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
@@ -349,18 +333,26 @@ public class ActionBarView extends AbsActionBarView {
mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
}
}
- super.setSplitActionBar(splitActionBar);
+ super.setSplitToolbar(splitActionBar);
}
}
- public boolean isSplitActionBar() {
+ public boolean isSplit() {
return mSplitActionBar;
}
+ public boolean canSplit() {
+ return true;
+ }
+
public boolean hasEmbeddedTabs() {
return mIncludeTabs;
}
+ public void setEmbeddedTabView(View view) {
+ setEmbeddedTabView((ScrollingTabContainerView) view);
+ }
+
public void setEmbeddedTabView(ScrollingTabContainerView tabs) {
if (mTabScrollView != null) {
removeView(mTabScrollView);
@@ -376,10 +368,6 @@ public class ActionBarView extends AbsActionBarView {
}
}
- public void setCallback(OnNavigationListener callback) {
- mCallback = callback;
- }
-
public void setMenuPrepared() {
mMenuPrepared = true;
}
@@ -430,6 +418,7 @@ public class ActionBarView extends AbsActionBarView {
mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
// Span the whole width
layoutParams.width = LayoutParams.MATCH_PARENT;
+ layoutParams.height = LayoutParams.WRAP_CONTENT;
configPresenters(builder);
menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
if (mSplitView != null) {
@@ -472,7 +461,7 @@ public class ActionBarView extends AbsActionBarView {
}
}
- public void setCustomNavigationView(View view) {
+ public void setCustomView(View view) {
final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
if (showCustom) {
ActionBarTransition.beginDelayedTransition(this);
@@ -696,7 +685,7 @@ public class ActionBarView extends AbsActionBarView {
}
public void setIcon(int resId) {
- setIcon(resId != 0 ? mContext.getResources().getDrawable(resId) : null);
+ setIcon(resId != 0 ? mContext.getDrawable(resId) : null);
}
public boolean hasIcon() {
@@ -711,7 +700,7 @@ public class ActionBarView extends AbsActionBarView {
}
public void setLogo(int resId) {
- setLogo(resId != 0 ? mContext.getResources().getDrawable(resId) : null);
+ setLogo(resId != 0 ? mContext.getDrawable(resId) : null);
}
public boolean hasLogo() {
@@ -764,15 +753,16 @@ public class ActionBarView extends AbsActionBarView {
}
}
- public void setDropdownAdapter(SpinnerAdapter adapter) {
+ public void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener l) {
mSpinnerAdapter = adapter;
+ mNavItemSelectedListener = l;
if (mSpinner != null) {
mSpinner.setAdapter(adapter);
}
}
- public SpinnerAdapter getDropdownAdapter() {
- return mSpinnerAdapter;
+ public int getDropdownItemCount() {
+ return mSpinnerAdapter != null ? mSpinnerAdapter.getCount() : 0;
}
public void setDropdownSelectedPosition(int position) {
@@ -783,7 +773,7 @@ public class ActionBarView extends AbsActionBarView {
return mSpinner.getSelectedItemPosition();
}
- public View getCustomNavigationView() {
+ public View getCustomView() {
return mCustomNavView;
}
@@ -796,6 +786,11 @@ public class ActionBarView extends AbsActionBarView {
}
@Override
+ public ViewGroup getViewGroup() {
+ return this;
+ }
+
+ @Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
// Used by custom nav views if they don't supply layout params. Everything else
// added to an ActionBarView should have them already.
@@ -859,12 +854,8 @@ public class ActionBarView extends AbsActionBarView {
mContextView = view;
}
- public void setCollapsable(boolean collapsable) {
- mIsCollapsable = collapsable;
- }
-
- public boolean isCollapsed() {
- return mIsCollapsed;
+ public void setCollapsible(boolean collapsible) {
+ mIsCollapsible = collapsible;
}
/**
@@ -892,7 +883,7 @@ public class ActionBarView extends AbsActionBarView {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int childCount = getChildCount();
- if (mIsCollapsable) {
+ if (mIsCollapsible) {
int visibleChildren = 0;
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
@@ -914,11 +905,9 @@ public class ActionBarView extends AbsActionBarView {
if (visibleChildren == 0) {
// No size for an empty action bar when collapsable.
setMeasuredDimension(0, 0);
- mIsCollapsed = true;
return;
}
}
- mIsCollapsed = false;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
@@ -1322,20 +1311,20 @@ public class ActionBarView extends AbsActionBarView {
}
}
- public void setHomeAsUpIndicator(Drawable indicator) {
+ public void setNavigationIcon(Drawable indicator) {
mHomeLayout.setUpIndicator(indicator);
}
- public void setHomeAsUpIndicator(int resId) {
+ public void setNavigationIcon(int resId) {
mHomeLayout.setUpIndicator(resId);
}
- public void setHomeActionContentDescription(CharSequence description) {
+ public void setNavigationContentDescription(CharSequence description) {
mHomeDescription = description;
updateHomeAccessibility(mUpGoerFive.isEnabled());
}
- public void setHomeActionContentDescription(int resId) {
+ public void setNavigationContentDescription(int resId) {
mHomeDescriptionRes = resId;
mHomeDescription = resId != 0 ? getResources().getText(resId) : null;
updateHomeAccessibility(mUpGoerFive.isEnabled());
@@ -1416,7 +1405,7 @@ public class ActionBarView extends AbsActionBarView {
public void setUpIndicator(int resId) {
mUpIndicatorRes = resId;
- mUpView.setImageDrawable(resId != 0 ? getResources().getDrawable(resId) : null);
+ mUpView.setImageDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
}
@Override
diff --git a/core/java/com/android/internal/widget/AutoScrollHelper.java b/core/java/com/android/internal/widget/AutoScrollHelper.java
index 7a294aa..0d468ca 100644
--- a/core/java/com/android/internal/widget/AutoScrollHelper.java
+++ b/core/java/com/android/internal/widget/AutoScrollHelper.java
@@ -892,6 +892,10 @@ public abstract class AutoScrollHelper implements View.OnTouchListener {
public boolean canTargetScrollVertically(int direction) {
final AbsListView target = mTarget;
final int itemCount = target.getCount();
+ if (itemCount == 0) {
+ return false;
+ }
+
final int childCount = target.getChildCount();
final int firstPosition = target.getFirstVisiblePosition();
final int lastPosition = firstPosition + childCount;
diff --git a/core/java/com/android/internal/widget/DecorContentParent.java b/core/java/com/android/internal/widget/DecorContentParent.java
new file mode 100644
index 0000000..4fa370a
--- /dev/null
+++ b/core/java/com/android/internal/widget/DecorContentParent.java
@@ -0,0 +1,53 @@
+/*
+ * 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.internal.widget;
+
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.util.SparseArray;
+import android.view.Menu;
+import android.view.Window;
+import com.android.internal.view.menu.MenuPresenter;
+
+/**
+ * Implemented by the top-level decor layout for a window. DecorContentParent offers
+ * entry points for a number of title/window decor features.
+ */
+public interface DecorContentParent {
+ void setWindowCallback(Window.Callback cb);
+ void setWindowTitle(CharSequence title);
+ CharSequence getTitle();
+ void initFeature(int windowFeature);
+ void setUiOptions(int uiOptions);
+ boolean hasIcon();
+ boolean hasLogo();
+ void setIcon(int resId);
+ void setIcon(Drawable d);
+ void setLogo(int resId);
+ boolean canShowOverflowMenu();
+ boolean isOverflowMenuShowing();
+ boolean isOverflowMenuShowPending();
+ boolean showOverflowMenu();
+ boolean hideOverflowMenu();
+ void setMenuPrepared();
+ void setMenu(Menu menu, MenuPresenter.Callback cb);
+ void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
+ void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
+ void dismissPopups();
+
+}
diff --git a/core/java/com/android/internal/widget/DecorToolbar.java b/core/java/com/android/internal/widget/DecorToolbar.java
new file mode 100644
index 0000000..ee6988e
--- /dev/null
+++ b/core/java/com/android/internal/widget/DecorToolbar.java
@@ -0,0 +1,94 @@
+/*
+ * 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.internal.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.util.SparseArray;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.SpinnerAdapter;
+import com.android.internal.view.menu.MenuPresenter;
+
+/**
+ * Common interface for a toolbar that sits as part of the window decor.
+ * Layouts that control window decor use this as a point of interaction with different
+ * bar implementations.
+ *
+ * @hide
+ */
+public interface DecorToolbar {
+ ViewGroup getViewGroup();
+ Context getContext();
+ boolean isSplit();
+ boolean hasExpandedActionView();
+ void collapseActionView();
+ void setWindowCallback(Window.Callback cb);
+ void setWindowTitle(CharSequence title);
+ CharSequence getTitle();
+ void setTitle(CharSequence title);
+ CharSequence getSubtitle();
+ void setSubtitle(CharSequence subtitle);
+ void initProgress();
+ void initIndeterminateProgress();
+ boolean canSplit();
+ void setSplitView(ViewGroup splitView);
+ void setSplitToolbar(boolean split);
+ void setSplitWhenNarrow(boolean splitWhenNarrow);
+ boolean hasIcon();
+ boolean hasLogo();
+ void setIcon(int resId);
+ void setIcon(Drawable d);
+ void setLogo(int resId);
+ void setLogo(Drawable d);
+ boolean canShowOverflowMenu();
+ boolean isOverflowMenuShowing();
+ boolean isOverflowMenuShowPending();
+ boolean showOverflowMenu();
+ boolean hideOverflowMenu();
+ void setMenuPrepared();
+ void setMenu(Menu menu, MenuPresenter.Callback cb);
+ void dismissPopupMenus();
+
+ int getDisplayOptions();
+ void setDisplayOptions(int opts);
+ void setEmbeddedTabView(View tabView);
+ boolean hasEmbeddedTabs();
+ boolean isTitleTruncated();
+ void setCollapsible(boolean collapsible);
+ void setHomeButtonEnabled(boolean enable);
+ int getNavigationMode();
+ void setNavigationMode(int mode);
+ void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener listener);
+ void setDropdownSelectedPosition(int position);
+ int getDropdownSelectedPosition();
+ int getDropdownItemCount();
+ void setCustomView(View view);
+ View getCustomView();
+ void animateToVisibility(int visibility);
+ void setNavigationIcon(Drawable icon);
+ void setNavigationIcon(int resId);
+ void setNavigationContentDescription(CharSequence description);
+ void setNavigationContentDescription(int resId);
+ void saveHierarchyState(SparseArray<Parcelable> toolbarStates);
+ void restoreHierarchyState(SparseArray<Parcelable> toolbarStates);
+}
diff --git a/core/java/com/android/internal/widget/DialogTitle.java b/core/java/com/android/internal/widget/DialogTitle.java
index b86c438..7ea3d6b 100644
--- a/core/java/com/android/internal/widget/DialogTitle.java
+++ b/core/java/com/android/internal/widget/DialogTitle.java
@@ -28,10 +28,13 @@ import android.widget.TextView;
* the text to the available space.
*/
public class DialogTitle extends TextView {
-
- public DialogTitle(Context context, AttributeSet attrs,
- int defStyle) {
- super(context, attrs, defStyle);
+
+ public DialogTitle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public DialogTitle(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
}
public DialogTitle(Context context, AttributeSet attrs) {
diff --git a/core/java/com/android/internal/widget/FaceUnlockView.java b/core/java/com/android/internal/widget/FaceUnlockView.java
index e3c1247..121e601 100644
--- a/core/java/com/android/internal/widget/FaceUnlockView.java
+++ b/core/java/com/android/internal/widget/FaceUnlockView.java
@@ -18,8 +18,6 @@ package com.android.internal.widget;
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
import android.widget.RelativeLayout;
public class FaceUnlockView extends RelativeLayout {
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 91056f1..c70841b 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -16,6 +16,8 @@
package com.android.internal.widget;
+import com.android.internal.widget.ILockSettingsObserver;
+
/** {@hide} */
interface ILockSettings {
void setBoolean(in String key, in boolean value, in int userId);
@@ -28,7 +30,10 @@ interface ILockSettings {
boolean checkPattern(in String pattern, int userId);
void setLockPassword(in String password, int userId);
boolean checkPassword(in String password, int userId);
+ boolean checkVoldPassword(int userId);
boolean havePattern(int userId);
boolean havePassword(int userId);
void removeUser(int userId);
+ void registerObserver(in ILockSettingsObserver observer);
+ void unregisterObserver(in ILockSettingsObserver observer);
}
diff --git a/core/java/com/android/internal/widget/ILockSettingsObserver.aidl b/core/java/com/android/internal/widget/ILockSettingsObserver.aidl
new file mode 100644
index 0000000..6c354d8
--- /dev/null
+++ b/core/java/com/android/internal/widget/ILockSettingsObserver.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.internal.widget;
+
+/** {@hide} */
+oneway interface ILockSettingsObserver {
+ void onLockSettingChanged(in String key, in int userId);
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 8602260..d31c5cc 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -19,18 +19,21 @@ package com.android.internal.widget;
import android.Manifest;
import android.app.ActivityManagerNative;
import android.app.admin.DevicePolicyManager;
+import android.app.trust.TrustManager;
import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.os.Binder;
+import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -40,12 +43,13 @@ import android.view.View;
import android.widget.Button;
import com.android.internal.R;
-import com.android.internal.telephony.ITelephony;
import com.google.android.collect.Lists;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
/**
@@ -145,6 +149,8 @@ public class LockPatternUtils {
private static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
+ private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
+
private final Context mContext;
private final ContentResolver mContentResolver;
private DevicePolicyManager mDevicePolicyManager;
@@ -167,6 +173,15 @@ public class LockPatternUtils {
return mDevicePolicyManager;
}
+ private TrustManager getTrustManager() {
+ TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
+ if (trust == null) {
+ Log.e(TAG, "Can't get TrustManagerService: is it running?",
+ new IllegalStateException("Stack trace:"));
+ }
+ return trust;
+ }
+
/**
* @param contentResolver Used to look up and save settings.
*/
@@ -183,8 +198,8 @@ public class LockPatternUtils {
private ILockSettings getLockSettings() {
if (mLockSettingsService == null) {
- mLockSettingsService = ILockSettings.Stub.asInterface(
- (IBinder) ServiceManager.getService("lock_settings"));
+ mLockSettingsService = LockPatternUtilsCache.getInstance(
+ ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings")));
}
return mLockSettingsService;
}
@@ -242,10 +257,14 @@ public class LockPatternUtils {
*/
public void reportFailedPasswordAttempt() {
getDevicePolicyManager().reportFailedPasswordAttempt(getCurrentOrCallingUserId());
+ getTrustManager().reportUnlockAttempt(false /* authenticated */,
+ getCurrentOrCallingUserId());
}
public void reportSuccessfulPasswordAttempt() {
getDevicePolicyManager().reportSuccessfulPasswordAttempt(getCurrentOrCallingUserId());
+ getTrustManager().reportUnlockAttempt(true /* authenticated */,
+ getCurrentOrCallingUserId());
}
public void setCurrentUser(int userId) {
@@ -313,6 +332,20 @@ public class LockPatternUtils {
}
/**
+ * Check to see if vold already has the password.
+ * Note that this also clears vold's copy of the password.
+ * @return Whether the vold password matches or not.
+ */
+ public boolean checkVoldPassword() {
+ final int userId = getCurrentOrCallingUserId();
+ try {
+ return getLockSettings().checkVoldPassword(userId);
+ } catch (RemoteException re) {
+ return false;
+ }
+ }
+
+ /**
* Check to see if a password matches any of the passwords stored in the
* password history.
*
@@ -496,38 +529,71 @@ public class LockPatternUtils {
*/
public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) {
try {
- getLockSettings().setLockPattern(patternToString(pattern), getCurrentOrCallingUserId());
+ int userId = getCurrentOrCallingUserId();
+ getLockSettings().setLockPattern(patternToString(pattern), userId);
DevicePolicyManager dpm = getDevicePolicyManager();
if (pattern != null) {
+
+ int userHandle = userId;
+ if (userHandle == UserHandle.USER_OWNER) {
+ String stringPattern = patternToString(pattern);
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
+ }
+
setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
if (!isFallback) {
deleteGallery();
setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
- pattern.size(), 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId());
+ pattern.size(), 0, 0, 0, 0, 0, 0, userId);
} else {
setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
setLong(PASSWORD_TYPE_ALTERNATE_KEY,
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
finishBiometricWeak();
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
- 0, 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId());
+ 0, 0, 0, 0, 0, 0, 0, userId);
}
} else {
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
- 0, 0, 0, 0, 0, getCurrentOrCallingUserId());
+ 0, 0, 0, 0, 0, userId);
}
} catch (RemoteException re) {
Log.e(TAG, "Couldn't save lock pattern " + re);
}
}
+ private void updateCryptoUserInfo() {
+ int userId = getCurrentOrCallingUserId();
+ if (userId != UserHandle.USER_OWNER) {
+ return;
+ }
+
+ final String ownerInfo = isOwnerInfoEnabled() ? getOwnerInfo(userId) : "";
+
+ IBinder service = ServiceManager.getService("mount");
+ if (service == null) {
+ Log.e(TAG, "Could not find the mount service to update the user info");
+ return;
+ }
+
+ IMountService mountService = IMountService.Stub.asInterface(service);
+ try {
+ Log.d(TAG, "Setting owner info");
+ mountService.setField("OwnerInfo", ownerInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error changing user info", e);
+ }
+ }
+
public void setOwnerInfo(String info, int userId) {
setString(LOCK_SCREEN_OWNER_INFO, info, userId);
+ updateCryptoUserInfo();
}
public void setOwnerInfoEnabled(boolean enabled) {
setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled);
+ updateCryptoUserInfo();
}
public String getOwnerInfo(int userId) {
@@ -566,7 +632,7 @@ public class LockPatternUtils {
}
/** Update the encryption password if it is enabled **/
- private void updateEncryptionPassword(String password) {
+ private void updateEncryptionPassword(int type, String password) {
DevicePolicyManager dpm = getDevicePolicyManager();
if (dpm.getStorageEncryptionStatus(getCurrentOrCallingUserId())
!= DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) {
@@ -581,7 +647,7 @@ public class LockPatternUtils {
IMountService mountService = IMountService.Stub.asInterface(service);
try {
- mountService.changeEncryptionPassword(password);
+ mountService.changeEncryptionPassword(type, password);
} catch (RemoteException e) {
Log.e(TAG, "Error changing encryption password", e);
}
@@ -624,12 +690,15 @@ public class LockPatternUtils {
getLockSettings().setLockPassword(password, userHandle);
DevicePolicyManager dpm = getDevicePolicyManager();
if (password != null) {
+ int computedQuality = computePasswordQuality(password);
+
if (userHandle == UserHandle.USER_OWNER) {
// Update the encryption password.
- updateEncryptionPassword(password);
+ int type = computedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD;
+ updateEncryptionPassword(type, password);
}
- int computedQuality = computePasswordQuality(password);
if (!isFallback) {
deleteGallery();
setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
@@ -676,8 +745,7 @@ public class LockPatternUtils {
0, 0, 0, 0, 0, 0, 0, userHandle);
}
// Add the password to the password history. We assume all
- // password
- // hashes have the same length for simplicity of implementation.
+ // password hashes have the same length for simplicity of implementation.
String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
if (passwordHistory == null) {
passwordHistory = new String();
@@ -696,6 +764,11 @@ public class LockPatternUtils {
}
setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
} else {
+ if (userHandle == UserHandle.USER_OWNER) {
+ // Update the encryption password.
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, password);
+ }
+
dpm.setActivePasswordState(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0,
userHandle);
@@ -714,13 +787,13 @@ public class LockPatternUtils {
*/
public int getKeyguardStoredPasswordQuality() {
int quality =
- (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+ (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
// If the user has chosen to use weak biometric sensor, then return the backup locking
// method and treat biometric as a special case.
if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
quality =
(int) getLong(PASSWORD_TYPE_ALTERNATE_KEY,
- DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
}
return quality;
}
@@ -730,7 +803,7 @@ public class LockPatternUtils {
*/
public boolean usingBiometricWeak() {
int quality =
- (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+ (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
}
@@ -869,11 +942,12 @@ public class LockPatternUtils {
*/
public boolean isLockPatternEnabled() {
final boolean backupEnabled =
- getLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
- == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+ getLong(PASSWORD_TYPE_ALTERNATE_KEY,
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
+ == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false)
- && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
+ && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
== DevicePolicyManager.PASSWORD_QUALITY_SOMETHING ||
(usingBiometricWeak() && backupEnabled));
}
@@ -1201,7 +1275,7 @@ public class LockPatternUtils {
private void setLong(String secureSettingKey, long value, int userHandle) {
try {
- getLockSettings().setLong(secureSettingKey, value, getCurrentOrCallingUserId());
+ getLockSettings().setLong(secureSettingKey, value, userHandle);
} catch (RemoteException re) {
// What can we do?
Log.e(TAG, "Couldn't write long " + secureSettingKey + re);
@@ -1285,19 +1359,11 @@ public class LockPatternUtils {
/**
* Resumes a call in progress. Typically launched from the EmergencyCall button
* on various lockscreens.
- *
- * @return true if we were able to tell InCallScreen to show.
*/
- public boolean resumeCall() {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- try {
- if (phone != null && phone.showCallScreen()) {
- return true;
- }
- } catch (RemoteException e) {
- // What can we do?
- }
- return false;
+ public void resumeCall() {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ telephonyManager.showCallScreen();
}
private void finishBiometricWeak() {
@@ -1360,4 +1426,38 @@ public class LockPatternUtils {
setBoolean(LOCKSCREEN_WIDGETS_ENABLED, enabled, userId);
}
+ public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents) {
+ setEnabledTrustAgents(activeTrustAgents, getCurrentOrCallingUserId());
+ }
+
+ public List<ComponentName> getEnabledTrustAgents() {
+ return getEnabledTrustAgents(getCurrentOrCallingUserId());
+ }
+
+ public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents, int userId) {
+ StringBuilder sb = new StringBuilder();
+ for (ComponentName cn : activeTrustAgents) {
+ if (sb.length() > 0) {
+ sb.append(',');
+ }
+ sb.append(cn.flattenToShortString());
+ }
+ setString(ENABLED_TRUST_AGENTS, sb.toString(), userId);
+ getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId());
+ }
+
+ public List<ComponentName> getEnabledTrustAgents(int userId) {
+ String serialized = getString(ENABLED_TRUST_AGENTS, userId);
+ if (TextUtils.isEmpty(serialized)) {
+ return null;
+ }
+ String[] split = serialized.split(",");
+ ArrayList<ComponentName> activeTrustAgents = new ArrayList<ComponentName>(split.length);
+ for (String s : split) {
+ if (!TextUtils.isEmpty(s)) {
+ activeTrustAgents.add(ComponentName.unflattenFromString(s));
+ }
+ }
+ return activeTrustAgents;
+ }
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtilsCache.java b/core/java/com/android/internal/widget/LockPatternUtilsCache.java
new file mode 100644
index 0000000..624f67c
--- /dev/null
+++ b/core/java/com/android/internal/widget/LockPatternUtilsCache.java
@@ -0,0 +1,243 @@
+/*
+ * 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.internal.widget;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+/**
+ * A decorator for {@link ILockSettings} that caches the key-value responses in memory.
+ *
+ * Specifically, the return values of {@link #getString(String, String, int)},
+ * {@link #getLong(String, long, int)} and {@link #getBoolean(String, boolean, int)} are cached.
+ */
+public class LockPatternUtilsCache implements ILockSettings {
+
+ private static final String HAS_LOCK_PATTERN_CACHE_KEY
+ = "LockPatternUtils.Cache.HasLockPatternCacheKey";
+ private static final String HAS_LOCK_PASSWORD_CACHE_KEY
+ = "LockPatternUtils.Cache.HasLockPasswordCacheKey";
+
+ private static LockPatternUtilsCache sInstance;
+
+ private final ILockSettings mService;
+
+ /** Only access when holding {@code mCache} lock. */
+ private final ArrayMap<CacheKey, Object> mCache = new ArrayMap<>();
+
+ /** Only access when holding {@link #mCache} lock. */
+ private final CacheKey mCacheKey = new CacheKey();
+
+
+ public static synchronized LockPatternUtilsCache getInstance(ILockSettings service) {
+ if (sInstance == null) {
+ sInstance = new LockPatternUtilsCache(service);
+ }
+ return sInstance;
+ }
+
+ // ILockSettings
+
+ private LockPatternUtilsCache(ILockSettings service) {
+ mService = service;
+ try {
+ service.registerObserver(mObserver);
+ } catch (RemoteException e) {
+ // Not safe to do caching without the observer. System process has probably died
+ // anyway, so crashing here is fine.
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setBoolean(String key, boolean value, int userId) throws RemoteException {
+ invalidateCache(key, userId);
+ mService.setBoolean(key, value, userId);
+ putCache(key, userId, value);
+ }
+
+ public void setLong(String key, long value, int userId) throws RemoteException {
+ invalidateCache(key, userId);
+ mService.setLong(key, value, userId);
+ putCache(key, userId, value);
+ }
+
+ public void setString(String key, String value, int userId) throws RemoteException {
+ invalidateCache(key, userId);
+ mService.setString(key, value, userId);
+ putCache(key, userId, value);
+ }
+
+ public long getLong(String key, long defaultValue, int userId) throws RemoteException {
+ Object value = peekCache(key, userId);
+ if (value instanceof Long) {
+ return (long) value;
+ }
+ long result = mService.getLong(key, defaultValue, userId);
+ putCache(key, userId, result);
+ return result;
+ }
+
+ public String getString(String key, String defaultValue, int userId) throws RemoteException {
+ Object value = peekCache(key, userId);
+ if (value instanceof String) {
+ return (String) value;
+ }
+ String result = mService.getString(key, defaultValue, userId);
+ putCache(key, userId, result);
+ return result;
+ }
+
+ public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
+ Object value = peekCache(key, userId);
+ if (value instanceof Boolean) {
+ return (boolean) value;
+ }
+ boolean result = mService.getBoolean(key, defaultValue, userId);
+ putCache(key, userId, result);
+ return result;
+ }
+
+ @Override
+ public void setLockPattern(String pattern, int userId) throws RemoteException {
+ invalidateCache(HAS_LOCK_PATTERN_CACHE_KEY, userId);
+ mService.setLockPattern(pattern, userId);
+ putCache(HAS_LOCK_PATTERN_CACHE_KEY, userId, pattern != null);
+ }
+
+ @Override
+ public boolean checkPattern(String pattern, int userId) throws RemoteException {
+ return mService.checkPattern(pattern, userId);
+ }
+
+ @Override
+ public void setLockPassword(String password, int userId) throws RemoteException {
+ invalidateCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId);
+ mService.setLockPassword(password, userId);
+ putCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId, password != null);
+ }
+
+ @Override
+ public boolean checkPassword(String password, int userId) throws RemoteException {
+ return mService.checkPassword(password, userId);
+ }
+
+ @Override
+ public boolean checkVoldPassword(int userId) throws RemoteException {
+ return mService.checkVoldPassword(userId);
+ }
+
+ @Override
+ public boolean havePattern(int userId) throws RemoteException {
+ Object value = peekCache(HAS_LOCK_PATTERN_CACHE_KEY, userId);
+ if (value instanceof Boolean) {
+ return (boolean) value;
+ }
+ boolean result = mService.havePattern(userId);
+ putCache(HAS_LOCK_PATTERN_CACHE_KEY, userId, result);
+ return result;
+ }
+
+ @Override
+ public boolean havePassword(int userId) throws RemoteException {
+ Object value = peekCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId);
+ if (value instanceof Boolean) {
+ return (boolean) value;
+ }
+ boolean result = mService.havePassword(userId);
+ putCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId, result);
+ return result;
+ }
+
+ @Override
+ public void removeUser(int userId) throws RemoteException {
+ mService.removeUser(userId);
+ }
+
+ @Override
+ public void registerObserver(ILockSettingsObserver observer) throws RemoteException {
+ mService.registerObserver(observer);
+ }
+
+ @Override
+ public void unregisterObserver(ILockSettingsObserver observer) throws RemoteException {
+ mService.unregisterObserver(observer);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mService.asBinder();
+ }
+
+ // Caching
+
+ private Object peekCache(String key, int userId) {
+ synchronized (mCache) {
+ // Safe to reuse mCacheKey, because it is not stored in the map.
+ return mCache.get(mCacheKey.set(key, userId));
+ }
+ }
+
+ private void putCache(String key, int userId, Object value) {
+ synchronized (mCache) {
+ // Create a new key, because this will be stored in the map.
+ mCache.put(new CacheKey().set(key, userId), value);
+ }
+ }
+
+ private void invalidateCache(String key, int userId) {
+ synchronized (mCache) {
+ // Safe to reuse mCacheKey, because it is not stored in the map.
+ mCache.remove(mCacheKey.set(key, userId));
+ }
+ }
+
+ private final ILockSettingsObserver mObserver = new ILockSettingsObserver.Stub() {
+ @Override
+ public void onLockSettingChanged(String key, int userId) throws RemoteException {
+ invalidateCache(key, userId);
+ }
+ };
+
+ private static final class CacheKey {
+ String key;
+ int userId;
+
+ public CacheKey set(String key, int userId) {
+ this.key = key;
+ this.userId = userId;
+ return this;
+ }
+
+ public CacheKey copy() {
+ return new CacheKey().set(key, userId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CacheKey))
+ return false;
+ CacheKey o = (CacheKey) obj;
+ return userId == o.userId && key.equals(o.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode() ^ userId;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index b066d70..d841d53 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -32,6 +32,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
@@ -56,6 +57,7 @@ public class LockPatternView extends View {
private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)
private static final boolean PROFILE_DRAWING = false;
+ private final CellState[][] mCellStates;
private boolean mDrawingProfilingStarted = false;
private Paint mPaint = new Paint();
@@ -186,6 +188,12 @@ public class LockPatternView extends View {
}
}
+ public static class CellState {
+ public float scale = 1.0f;
+ public float translateY = 0.0f;
+ public float alpha = 1.0f;
+ }
+
/**
* How to display the current pattern.
*/
@@ -260,13 +268,23 @@ public class LockPatternView extends View {
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true);
- mPathPaint.setColor(Color.WHITE); // TODO this should be from the style
+
+ int defaultColor = Color.WHITE;
+ TypedValue outValue = new TypedValue();
+ if (context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, outValue, true)) {
+ defaultColor = context.getResources().getColor(outValue.resourceId);
+ }
+
+ final int color = a.getColor(R.styleable.LockPatternView_pathColor, defaultColor);
+ mPathPaint.setColor(color);
+
mPathPaint.setAlpha(mStrokeAlpha);
mPathPaint.setStyle(Paint.Style.STROKE);
mPathPaint.setStrokeJoin(Paint.Join.ROUND);
mPathPaint.setStrokeCap(Paint.Cap.ROUND);
// lot's of bitmaps!
+ // TODO: those bitmaps are hardcoded to the Holo Theme which should not be the case!
mBitmapBtnDefault = getBitmapFor(R.drawable.btn_code_lock_default_holo);
mBitmapBtnTouched = getBitmapFor(R.drawable.btn_code_lock_touched_holo);
mBitmapCircleDefault = getBitmapFor(R.drawable.indicator_code_lock_point_area_default_holo);
@@ -285,6 +303,18 @@ public class LockPatternView extends View {
mBitmapHeight = Math.max(mBitmapHeight, bitmap.getHeight());
}
+ mPaint.setFilterBitmap(true);
+
+ mCellStates = new CellState[3][3];
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ mCellStates[i][j] = new CellState();
+ }
+ }
+ }
+
+ public CellState[][] getCellStates() {
+ return mCellStates;
}
private Bitmap getBitmapFor(int resId) {
@@ -862,20 +892,22 @@ public class LockPatternView extends View {
//float centerY = mPaddingTop + i * mSquareHeight + (mSquareHeight / 2);
for (int j = 0; j < 3; j++) {
float leftX = paddingLeft + j * squareWidth;
- drawCircle(canvas, (int) leftX, (int) topY, drawLookup[i][j]);
+ float scale = mCellStates[i][j].scale;
+ mPaint.setAlpha((int) (mCellStates[i][j].alpha * 255));
+ float translationY = mCellStates[i][j].translateY;
+ drawCircle(canvas, (int) leftX, (int) topY + translationY, scale, drawLookup[i][j]);
}
}
+ // Reset the alpha to draw normally
+ mPaint.setAlpha(255);
+
// TODO: the path should be created and cached every time we hit-detect a cell
// only the last segment of the path should be computed here
- // draw the path of the pattern (unless the user is in progress, and
- // we are in stealth mode)
- final boolean drawPath = (!mInStealthMode || mPatternDisplayMode == DisplayMode.Wrong);
-
- // draw the arrows associated with the path (unless the user is in progress, and
- // we are in stealth mode)
- boolean oldFlag = (mPaint.getFlags() & Paint.FILTER_BITMAP_FLAG) != 0;
- mPaint.setFilterBitmap(true); // draw with higher quality since we render with transforms
+ // draw the path of the pattern (unless we are in stealth mode)
+ final boolean drawPath = !mInStealthMode;
+
+ // draw the arrows associated with the path (unless we are in stealth mode)
if (drawPath) {
for (int i = 0; i < count - 1; i++) {
Cell cell = pattern.get(i);
@@ -889,7 +921,8 @@ public class LockPatternView extends View {
}
float leftX = paddingLeft + cell.column * squareWidth;
- float topY = paddingTop + cell.row * squareHeight;
+ float topY = paddingTop + cell.row * squareHeight
+ + mCellStates[cell.row][cell.column].translateY;
drawArrow(canvas, leftX, topY, cell, next);
}
@@ -910,6 +943,9 @@ public class LockPatternView extends View {
float centerX = getCenterXForColumn(cell.column);
float centerY = getCenterYForRow(cell.row);
+
+ // Respect translation in animation
+ centerY += mCellStates[cell.row][cell.column].translateY;
if (i == 0) {
currentPath.moveTo(centerX, centerY);
} else {
@@ -924,8 +960,6 @@ public class LockPatternView extends View {
}
canvas.drawPath(currentPath, mPathPaint);
}
-
- mPaint.setFilterBitmap(oldFlag); // restore default flag
}
private void drawArrow(Canvas canvas, float leftX, float topY, Cell start, Cell end) {
@@ -970,11 +1004,12 @@ public class LockPatternView extends View {
* @param topY
* @param partOfPattern Whether this circle is part of the pattern.
*/
- private void drawCircle(Canvas canvas, int leftX, int topY, boolean partOfPattern) {
+ private void drawCircle(Canvas canvas, float leftX, float topY, float scale,
+ boolean partOfPattern) {
Bitmap outerCircle;
Bitmap innerCircle;
- if (!partOfPattern || (mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong)) {
+ if (!partOfPattern || mInStealthMode) {
// unselected circle
outerCircle = mBitmapCircleDefault;
innerCircle = mBitmapBtnDefault;
@@ -1010,7 +1045,7 @@ public class LockPatternView extends View {
mCircleMatrix.setTranslate(leftX + offsetX, topY + offsetY);
mCircleMatrix.preTranslate(mBitmapWidth/2, mBitmapHeight/2);
- mCircleMatrix.preScale(sx, sy);
+ mCircleMatrix.preScale(sx * scale, sy * scale);
mCircleMatrix.preTranslate(-mBitmapWidth/2, -mBitmapHeight/2);
canvas.drawBitmap(outerCircle, mCircleMatrix, mPaint);
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
index 3c01c69..7483e75 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
@@ -16,7 +16,6 @@
package com.android.internal.widget;
-import java.util.Locale;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -73,8 +72,8 @@ public class PasswordEntryKeyboard extends Keyboard {
private void init(Context context) {
final Resources res = context.getResources();
- mShiftIcon = res.getDrawable(R.drawable.sym_keyboard_shift);
- mShiftLockIcon = res.getDrawable(R.drawable.sym_keyboard_shift_locked);
+ mShiftIcon = context.getDrawable(R.drawable.sym_keyboard_shift);
+ mShiftLockIcon = context.getDrawable(R.drawable.sym_keyboard_shift_locked);
sSpacebarVerticalCorrection = res.getDimensionPixelOffset(
R.dimen.password_keyboard_spacebar_vertical_correction);
}
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
index a3df291..b2c9dc5 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
@@ -21,9 +21,7 @@ import android.content.res.Resources;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
-import android.os.Handler;
import android.os.SystemClock;
-import android.os.Vibrator;
import android.provider.Settings;
import android.util.Log;
import android.view.HapticFeedbackConstants;
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java
index b37adff..d27346b 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java
@@ -29,11 +29,16 @@ public class PasswordEntryKeyboardView extends KeyboardView {
static final int KEYCODE_NEXT_LANGUAGE = -104;
public PasswordEntryKeyboardView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
- public PasswordEntryKeyboardView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public PasswordEntryKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public PasswordEntryKeyboardView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index d82831f..e339c44 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -31,11 +31,13 @@ import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.WindowManagerPolicy.PointerEventListener;
import android.view.MotionEvent.PointerCoords;
import java.util.ArrayList;
-public class PointerLocationView extends View implements InputDeviceListener {
+public class PointerLocationView extends View implements InputDeviceListener,
+ PointerEventListener {
private static final String TAG = "Pointer";
// The system property key used to specify an alternate velocity tracker strategy
@@ -520,7 +522,8 @@ public class PointerLocationView extends View implements InputDeviceListener {
.toString());
}
- public void addPointerEvent(MotionEvent event) {
+ @Override
+ public void onPointerEvent(MotionEvent event) {
final int action = event.getAction();
int NP = mPointers.size();
@@ -648,7 +651,7 @@ public class PointerLocationView extends View implements InputDeviceListener {
@Override
public boolean onTouchEvent(MotionEvent event) {
- addPointerEvent(event);
+ onPointerEvent(event);
if (event.getAction() == MotionEvent.ACTION_DOWN && !isFocused()) {
requestFocus();
@@ -660,7 +663,7 @@ public class PointerLocationView extends View implements InputDeviceListener {
public boolean onGenericMotionEvent(MotionEvent event) {
final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- addPointerEvent(event);
+ onPointerEvent(event);
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
logMotionEvent("Joystick", event);
} else if ((source & InputDevice.SOURCE_CLASS_POSITION) != 0) {
diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java
index 4e405f4..64ce918 100644
--- a/core/java/com/android/internal/widget/RotarySelector.java
+++ b/core/java/com/android/internal/widget/RotarySelector.java
@@ -24,7 +24,7 @@ import android.graphics.Paint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
-import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
@@ -35,7 +35,9 @@ import android.view.View;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.animation.DecelerateInterpolator;
+
import static android.view.animation.AnimationUtils.currentAnimationTimeMillis;
+
import com.android.internal.R;
@@ -677,7 +679,7 @@ public class RotarySelector extends View {
mVibrator = (android.os.Vibrator) getContext()
.getSystemService(Context.VIBRATOR_SERVICE);
}
- mVibrator.vibrate(duration);
+ mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index fa29e6e..d6bd1d6 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -23,7 +23,6 @@ import android.animation.TimeInterpolator;
import android.app.ActionBar;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
diff --git a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
index ba113a3..5f3c5f9 100644
--- a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
+++ b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
@@ -79,17 +79,20 @@ public class SizeAdaptiveLayout extends ViewGroup {
private int mModestyPanelTop;
public SizeAdaptiveLayout(Context context) {
- super(context);
- initialize();
+ this(context, null);
}
public SizeAdaptiveLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- initialize();
+ this(context, attrs, 0);
+ }
+
+ public SizeAdaptiveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
}
- public SizeAdaptiveLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public SizeAdaptiveLayout(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
initialize();
}
@@ -104,8 +107,6 @@ public class SizeAdaptiveLayout extends ViewGroup {
}
if (background instanceof ColorDrawable) {
mModestyPanel.setBackgroundDrawable(background);
- } else {
- mModestyPanel.setBackgroundColor(Color.BLACK);
}
SizeAdaptiveLayout.LayoutParams layout =
new SizeAdaptiveLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
@@ -151,6 +152,10 @@ public class SizeAdaptiveLayout extends ViewGroup {
if (DEBUG) Log.d(TAG, this + " measure spec: " +
MeasureSpec.toString(heightMeasureSpec));
View model = selectActiveChild(heightMeasureSpec);
+ if (model == null) {
+ setMeasuredDimension(0, 0);
+ return;
+ }
SizeAdaptiveLayout.LayoutParams lp =
(SizeAdaptiveLayout.LayoutParams) model.getLayoutParams();
if (DEBUG) Log.d(TAG, "active min: " + lp.minHeight + " max: " + lp.maxHeight);
@@ -239,6 +244,8 @@ public class SizeAdaptiveLayout extends ViewGroup {
int measureSpec = View.MeasureSpec.makeMeasureSpec(bottom - top,
View.MeasureSpec.EXACTLY);
mActiveChild = selectActiveChild(measureSpec);
+ if (mActiveChild == null) return;
+
mActiveChild.setVisibility(View.VISIBLE);
if (mLastActive != mActiveChild && mLastActive != null) {
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index aebc4f6..deb0fd7 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -21,6 +21,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
@@ -821,7 +822,7 @@ public class SlidingTab extends ViewGroup {
mVibrator = (android.os.Vibrator) getContext()
.getSystemService(Context.VIBRATOR_SERVICE);
}
- mVibrator.vibrate(duration);
+ mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index 071193c..117463a 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -18,7 +18,6 @@ package com.android.internal.widget;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.res.Resources.Theme;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -33,7 +32,6 @@ import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
-import android.util.TypedValue;
import android.view.View;
import android.view.accessibility.CaptioningManager.CaptionStyle;
@@ -41,6 +39,12 @@ public class SubtitleView extends View {
// Ratio of inner padding to font size.
private static final float INNER_PADDING_RATIO = 0.125f;
+ /** Color used for the shadowed edge of a bevel. */
+ private static final int COLOR_BEVEL_DARK = 0x80000000;
+
+ /** Color used for the illuminated edge of a bevel. */
+ private static final int COLOR_BEVEL_LIGHT = 0x80FFFFFF;
+
// Styled dimensions.
private final float mCornerRadius;
private final float mOutlineWidth;
@@ -79,12 +83,15 @@ public class SubtitleView extends View {
this(context, attrs, 0);
}
- public SubtitleView(Context context, AttributeSet attrs, int defStyle) {
+ public SubtitleView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public SubtitleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs);
- final Theme theme = context.getTheme();
- final TypedArray a = theme.obtainStyledAttributes(
- attrs, android.R.styleable.TextView, defStyle, 0);
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, android.R.styleable.TextView, defStyleAttr, defStyleRes);
CharSequence text = "";
int textSize = 15;
@@ -112,7 +119,6 @@ public class SubtitleView extends View {
// Set up density-dependent properties.
// TODO: Move these to a default style.
final Resources res = getContext().getResources();
- final DisplayMetrics m = res.getDisplayMetrics();
mCornerRadius = res.getDimensionPixelSize(com.android.internal.R.dimen.subtitle_corner_radius);
mOutlineWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.subtitle_outline_width);
mShadowRadius = res.getDimensionPixelSize(com.android.internal.R.dimen.subtitle_shadow_radius);
@@ -311,7 +317,8 @@ public class SubtitleView extends View {
}
}
- if (mEdgeType == CaptionStyle.EDGE_TYPE_OUTLINE) {
+ final int edgeType = mEdgeType;
+ if (edgeType == CaptionStyle.EDGE_TYPE_OUTLINE) {
textPaint.setStrokeJoin(Join.ROUND);
textPaint.setStrokeWidth(mOutlineWidth);
textPaint.setColor(mEdgeColor);
@@ -320,8 +327,24 @@ public class SubtitleView extends View {
for (int i = 0; i < lineCount; i++) {
layout.drawText(c, i, i);
}
- } else if (mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
+ } else if (edgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
textPaint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mEdgeColor);
+ } else if (edgeType == CaptionStyle.EDGE_TYPE_RAISED
+ || edgeType == CaptionStyle.EDGE_TYPE_DEPRESSED) {
+ final boolean raised = edgeType == CaptionStyle.EDGE_TYPE_RAISED;
+ final int colorUp = raised ? Color.WHITE : mEdgeColor;
+ final int colorDown = raised ? mEdgeColor : Color.WHITE;
+ final float offset = mShadowRadius / 2f;
+
+ textPaint.setColor(mForegroundColor);
+ textPaint.setStyle(Style.FILL);
+ textPaint.setShadowLayer(mShadowRadius, -offset, -offset, colorUp);
+
+ for (int i = 0; i < lineCount; i++) {
+ layout.drawText(c, i, i);
+ }
+
+ textPaint.setShadowLayer(mShadowRadius, offset, offset, colorDown);
}
textPaint.setColor(mForegroundColor);
diff --git a/core/java/com/android/internal/widget/TextProgressBar.java b/core/java/com/android/internal/widget/TextProgressBar.java
index e898aa4..7ca07d4 100644
--- a/core/java/com/android/internal/widget/TextProgressBar.java
+++ b/core/java/com/android/internal/widget/TextProgressBar.java
@@ -19,7 +19,6 @@ package com.android.internal.widget;
import android.content.Context;
import android.os.SystemClock;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -59,9 +58,13 @@ public class TextProgressBar extends RelativeLayout implements OnChronometerTick
boolean mChronometerFollow = false;
int mChronometerGravity = Gravity.NO_GRAVITY;
+
+ public TextProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
- public TextProgressBar(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public TextProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
}
public TextProgressBar(Context context, AttributeSet attrs) {
diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
new file mode 100644
index 0000000..3e15c32
--- /dev/null
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -0,0 +1,541 @@
+/*
+ * 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.internal.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.ActionMenuPresenter;
+import android.widget.AdapterView;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.Toolbar;
+import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenuItem;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPresenter;
+
+/**
+ * Internal class used to interact with the Toolbar widget without
+ * exposing interface methods to the public API.
+ *
+ * <p>ToolbarWidgetWrapper manages the differences between Toolbar and ActionBarView
+ * so that either variant acting as a
+ * {@link com.android.internal.app.WindowDecorActionBar WindowDecorActionBar} can behave
+ * in the same way.</p>
+ *
+ * @hide
+ */
+public class ToolbarWidgetWrapper implements DecorToolbar {
+ private static final String TAG = "ToolbarWidgetWrapper";
+
+ private static final int AFFECTS_LOGO_MASK =
+ ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO;
+
+ private Toolbar mToolbar;
+
+ private int mDisplayOpts;
+ private View mTabView;
+ private Spinner mSpinner;
+ private View mCustomView;
+
+ private Drawable mIcon;
+ private Drawable mLogo;
+ private Drawable mNavIcon;
+
+ private boolean mTitleSet;
+ private CharSequence mTitle;
+ private CharSequence mSubtitle;
+
+ private Window.Callback mWindowCallback;
+ private boolean mMenuPrepared;
+ private ActionMenuPresenter mActionMenuPresenter;
+
+ public ToolbarWidgetWrapper(Toolbar toolbar) {
+ mToolbar = toolbar;
+
+ mTitle = toolbar.getTitle();
+ mSubtitle = toolbar.getSubtitle();
+ mTitleSet = !TextUtils.isEmpty(mTitle);
+
+ final TypedArray a = toolbar.getContext().obtainStyledAttributes(null,
+ R.styleable.ActionBar, R.attr.actionBarStyle, 0);
+
+ final CharSequence title = a.getText(R.styleable.ActionBar_title);
+ if (!TextUtils.isEmpty(title)) {
+ setTitle(title);
+ }
+
+ final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle);
+ if (!TextUtils.isEmpty(subtitle)) {
+ setSubtitle(subtitle);
+ }
+
+ final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo);
+ if (logo != null) {
+ setLogo(logo);
+ }
+
+ final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon);
+ if (icon != null) {
+ setIcon(icon);
+ }
+
+ final Drawable navIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator);
+ if (navIcon != null) {
+ setNavigationIcon(navIcon);
+ }
+
+ setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0));
+
+ final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
+ if (customNavId != 0) {
+ setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId,
+ mToolbar, false));
+ setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM);
+ }
+
+ final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
+ if (height > 0) {
+ final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams();
+ lp.height = height;
+ mToolbar.setLayoutParams(lp);
+ }
+
+ final int contentInsetStart = a.getDimensionPixelOffset(
+ R.styleable.ActionBar_contentInsetStart, 0);
+ final int contentInsetEnd = a.getDimensionPixelOffset(
+ R.styleable.ActionBar_contentInsetEnd, 0);
+ if (contentInsetStart > 0 || contentInsetEnd > 0) {
+ mToolbar.setContentInsetsRelative(contentInsetStart, contentInsetEnd);
+ }
+
+ final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
+ if (titleTextStyle != 0) {
+ mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle);
+ }
+
+ final int subtitleTextStyle = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0);
+ if (subtitleTextStyle != 0) {
+ mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle);
+ }
+
+ a.recycle();
+
+ mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
+ final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(),
+ 0, android.R.id.home, 0, 0, mTitle);
+ @Override
+ public void onClick(View v) {
+ if (mWindowCallback != null && mMenuPrepared) {
+ mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem);
+ }
+ }
+ });
+ }
+
+ @Override
+ public ViewGroup getViewGroup() {
+ return mToolbar;
+ }
+
+ @Override
+ public Context getContext() {
+ return mToolbar.getContext();
+ }
+
+ @Override
+ public boolean isSplit() {
+ return false;
+ }
+
+ @Override
+ public boolean hasExpandedActionView() {
+ return mToolbar.hasExpandedActionView();
+ }
+
+ @Override
+ public void collapseActionView() {
+ mToolbar.collapseActionView();
+ }
+
+ @Override
+ public void setWindowCallback(Window.Callback cb) {
+ mWindowCallback = cb;
+ }
+
+ @Override
+ public void setWindowTitle(CharSequence title) {
+ // "Real" title always trumps window title.
+ if (!mTitleSet) {
+ setTitleInt(title);
+ }
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mToolbar.getTitle();
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mTitleSet = true;
+ setTitleInt(title);
+ }
+
+ private void setTitleInt(CharSequence title) {
+ mTitle = title;
+ if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+ mToolbar.setTitle(title);
+ }
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mToolbar.getSubtitle();
+ }
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {
+ mSubtitle = subtitle;
+ if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+ mToolbar.setSubtitle(subtitle);
+ }
+ }
+
+ @Override
+ public void initProgress() {
+ Log.i(TAG, "Progress display unsupported");
+ }
+
+ @Override
+ public void initIndeterminateProgress() {
+ Log.i(TAG, "Progress display unsupported");
+ }
+
+ @Override
+ public boolean canSplit() {
+ return false;
+ }
+
+ @Override
+ public void setSplitView(ViewGroup splitView) {
+ }
+
+ @Override
+ public void setSplitToolbar(boolean split) {
+ if (split) {
+ throw new UnsupportedOperationException("Cannot split an android.widget.Toolbar");
+ }
+ }
+
+ @Override
+ public void setSplitWhenNarrow(boolean splitWhenNarrow) {
+ // Ignore.
+ }
+
+ @Override
+ public boolean hasIcon() {
+ return mIcon != null;
+ }
+
+ @Override
+ public boolean hasLogo() {
+ return mLogo != null;
+ }
+
+ @Override
+ public void setIcon(int resId) {
+ setIcon(resId != 0 ? getContext().getDrawable(resId) : null);
+ }
+
+ @Override
+ public void setIcon(Drawable d) {
+ mIcon = d;
+ updateToolbarLogo();
+ }
+
+ @Override
+ public void setLogo(int resId) {
+ setLogo(resId != 0 ? getContext().getDrawable(resId) : null);
+ }
+
+ @Override
+ public void setLogo(Drawable d) {
+ mLogo = d;
+ updateToolbarLogo();
+ }
+
+ private void updateToolbarLogo() {
+ Drawable logo = null;
+ if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_HOME) != 0) {
+ if ((mDisplayOpts & ActionBar.DISPLAY_USE_LOGO) != 0) {
+ logo = mLogo != null ? mLogo : mIcon;
+ } else {
+ logo = mIcon;
+ }
+ }
+ mToolbar.setLogo(logo);
+ }
+
+ @Override
+ public boolean canShowOverflowMenu() {
+ return mToolbar.canShowOverflowMenu();
+ }
+
+ @Override
+ public boolean isOverflowMenuShowing() {
+ return mToolbar.isOverflowMenuShowing();
+ }
+
+ @Override
+ public boolean isOverflowMenuShowPending() {
+ return mToolbar.isOverflowMenuShowPending();
+ }
+
+ @Override
+ public boolean showOverflowMenu() {
+ return mToolbar.showOverflowMenu();
+ }
+
+ @Override
+ public boolean hideOverflowMenu() {
+ return mToolbar.hideOverflowMenu();
+ }
+
+ @Override
+ public void setMenuPrepared() {
+ mMenuPrepared = true;
+ }
+
+ @Override
+ public void setMenu(Menu menu, MenuPresenter.Callback cb) {
+ if (mActionMenuPresenter == null) {
+ mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext());
+ mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
+ }
+ mActionMenuPresenter.setCallback(cb);
+ mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter);
+ }
+
+ @Override
+ public void dismissPopupMenus() {
+ mToolbar.dismissPopupMenus();
+ }
+
+ @Override
+ public int getDisplayOptions() {
+ return mDisplayOpts;
+ }
+
+ @Override
+ public void setDisplayOptions(int newOpts) {
+ final int oldOpts = mDisplayOpts;
+ final int changed = oldOpts ^ newOpts;
+ mDisplayOpts = newOpts;
+ if (changed != 0) {
+ if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+ if ((newOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+ mToolbar.setNavigationIcon(mNavIcon);
+ } else {
+ mToolbar.setNavigationIcon(null);
+ }
+ }
+
+ if ((changed & AFFECTS_LOGO_MASK) != 0) {
+ updateToolbarLogo();
+ }
+
+ if ((changed & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+ if ((newOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+ mToolbar.setTitle(mTitle);
+ mToolbar.setSubtitle(mSubtitle);
+ } else {
+ mToolbar.setTitle(null);
+ mToolbar.setSubtitle(null);
+ }
+ }
+
+ if ((changed & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomView != null) {
+ if ((newOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+ mToolbar.addView(mCustomView);
+ } else {
+ mToolbar.removeView(mCustomView);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setEmbeddedTabView(View tabView) {
+ mTabView = tabView;
+ }
+
+ @Override
+ public boolean hasEmbeddedTabs() {
+ return mTabView != null;
+ }
+
+ @Override
+ public boolean isTitleTruncated() {
+ return mToolbar.isTitleTruncated();
+ }
+
+ @Override
+ public void setCollapsible(boolean collapsible) {
+ // Ignore
+ }
+
+ @Override
+ public void setHomeButtonEnabled(boolean enable) {
+ // Ignore
+ }
+
+ @Override
+ public int getNavigationMode() {
+ return 0;
+ }
+
+ @Override
+ public void setNavigationMode(int mode) {
+ if (mode != ActionBar.NAVIGATION_MODE_STANDARD) {
+ throw new IllegalArgumentException(
+ "Navigation modes not supported in this configuration");
+ }
+ }
+
+ @Override
+ public void setDropdownParams(SpinnerAdapter adapter,
+ AdapterView.OnItemSelectedListener listener) {
+ if (mSpinner == null) {
+ mSpinner = new Spinner(getContext());
+ }
+ mSpinner.setAdapter(adapter);
+ mSpinner.setOnItemSelectedListener(listener);
+ }
+
+ @Override
+ public void setDropdownSelectedPosition(int position) {
+ if (mSpinner == null) {
+ throw new IllegalStateException(
+ "Can't set dropdown selected position without an adapter");
+ }
+ mSpinner.setSelection(position);
+ }
+
+ @Override
+ public int getDropdownSelectedPosition() {
+ return mSpinner != null ? mSpinner.getSelectedItemPosition() : 0;
+ }
+
+ @Override
+ public int getDropdownItemCount() {
+ return mSpinner != null ? mSpinner.getCount() : 0;
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ if (mCustomView != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+ mToolbar.removeView(mCustomView);
+ }
+ mCustomView = view;
+ if (view != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+ mToolbar.addView(mCustomView);
+ }
+ }
+
+ @Override
+ public View getCustomView() {
+ return mCustomView;
+ }
+
+ @Override
+ public void animateToVisibility(int visibility) {
+ if (visibility == View.GONE) {
+ mToolbar.animate().translationY(mToolbar.getHeight()).alpha(0)
+ .setListener(new AnimatorListenerAdapter() {
+ private boolean mCanceled = false;
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCanceled) {
+ mToolbar.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+ });
+ } else if (visibility == View.VISIBLE) {
+ mToolbar.animate().translationY(0).alpha(1)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mToolbar.setVisibility(View.VISIBLE);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void setNavigationIcon(Drawable icon) {
+ mNavIcon = icon;
+ if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+ mToolbar.setNavigationIcon(icon);
+ }
+ }
+
+ @Override
+ public void setNavigationIcon(int resId) {
+ setNavigationIcon(mToolbar.getContext().getDrawable(resId));
+ }
+
+ @Override
+ public void setNavigationContentDescription(CharSequence description) {
+ mToolbar.setNavigationContentDescription(description);
+ }
+
+ @Override
+ public void setNavigationContentDescription(int resId) {
+ mToolbar.setNavigationContentDescription(resId);
+ }
+
+ @Override
+ public void saveHierarchyState(SparseArray<Parcelable> toolbarStates) {
+ mToolbar.saveHierarchyState(toolbarStates);
+ }
+
+ @Override
+ public void restoreHierarchyState(SparseArray<Parcelable> toolbarStates) {
+ mToolbar.restoreHierarchyState(toolbarStates);
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
index d33d50c..86f14b3 100644
--- a/core/java/com/android/internal/widget/WaveView.java
+++ b/core/java/com/android/internal/widget/WaveView.java
@@ -25,10 +25,10 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
+import android.media.AudioManager;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
-import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@@ -583,7 +583,7 @@ public class WaveView extends View implements ValueAnimator.AnimatorUpdateListen
mVibrator = (android.os.Vibrator) getContext()
.getSystemService(Context.VIBRATOR_SERVICE);
}
- mVibrator.vibrate(duration);
+ mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
index cd1ccd3..841a02a 100644
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
@@ -30,6 +30,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.Vibrator;
@@ -234,9 +235,13 @@ public class GlowPadView extends View {
mMagneticTargets = a.getBoolean(R.styleable.GlowPadView_magneticTargets, mMagneticTargets);
int pointId = getResourceId(a, R.styleable.GlowPadView_pointDrawable);
- Drawable pointDrawable = pointId != 0 ? res.getDrawable(pointId) : null;
+ Drawable pointDrawable = pointId != 0 ? context.getDrawable(pointId) : null;
mGlowRadius = a.getDimension(R.styleable.GlowPadView_glowRadius, 0.0f);
+ mPointCloud = new PointCloud(pointDrawable);
+ mPointCloud.makePointCloud(mInnerRadius, mOuterRadius);
+ mPointCloud.glowManager.setRadius(mGlowRadius);
+
TypedValue outValue = new TypedValue();
// Read array of target drawables
@@ -272,10 +277,6 @@ public class GlowPadView extends View {
setVibrateEnabled(mVibrationDuration > 0);
assignDefaultsIfNeeded();
-
- mPointCloud = new PointCloud(pointDrawable);
- mPointCloud.makePointCloud(mInnerRadius, mOuterRadius);
- mPointCloud.glowManager.setRadius(mGlowRadius);
}
private int getResourceId(TypedArray a, int id) {
@@ -564,7 +565,7 @@ public class GlowPadView extends View {
mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
UserHandle.USER_CURRENT) != 0;
if (mVibrator != null && hapticEnabled) {
- mVibrator.vibrate(mVibrationDuration);
+ mVibrator.vibrate(mVibrationDuration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
deleted file mode 100644
index e22d1e8..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ /dev/null
@@ -1,1269 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.widget.multiwaveview;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-
-/**
- * A special widget containing a center and outer ring. Moving the center ring to the outer ring
- * causes an event that can be caught by implementing OnTriggerListener.
- */
-public class MultiWaveView extends View {
- private static final String TAG = "MultiWaveView";
- private static final boolean DEBUG = false;
-
- // Wave state machine
- private static final int STATE_IDLE = 0;
- private static final int STATE_START = 1;
- private static final int STATE_FIRST_TOUCH = 2;
- private static final int STATE_TRACKING = 3;
- private static final int STATE_SNAP = 4;
- private static final int STATE_FINISH = 5;
-
- // Animation properties.
- private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it
-
- public interface OnTriggerListener {
- int NO_HANDLE = 0;
- int CENTER_HANDLE = 1;
- public void onGrabbed(View v, int handle);
- public void onReleased(View v, int handle);
- public void onTrigger(View v, int target);
- public void onGrabbedStateChange(View v, int handle);
- public void onFinishFinalAnimation();
- }
-
- // Tuneable parameters for animation
- private static final int CHEVRON_INCREMENTAL_DELAY = 160;
- private static final int CHEVRON_ANIMATION_DURATION = 850;
- private static final int RETURN_TO_HOME_DELAY = 1200;
- private static final int RETURN_TO_HOME_DURATION = 200;
- private static final int HIDE_ANIMATION_DELAY = 200;
- private static final int HIDE_ANIMATION_DURATION = 200;
- private static final int SHOW_ANIMATION_DURATION = 200;
- private static final int SHOW_ANIMATION_DELAY = 50;
- private static final int INITIAL_SHOW_HANDLE_DURATION = 200;
-
- private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
- private static final float TARGET_SCALE_EXPANDED = 1.0f;
- private static final float TARGET_SCALE_COLLAPSED = 0.8f;
- private static final float RING_SCALE_EXPANDED = 1.0f;
- private static final float RING_SCALE_COLLAPSED = 0.5f;
-
- private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut;
-
- private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
- private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>();
- private AnimationBundle mChevronAnimations = new AnimationBundle();
- private AnimationBundle mTargetAnimations = new AnimationBundle();
- private AnimationBundle mHandleAnimations = new AnimationBundle();
- private ArrayList<String> mTargetDescriptions;
- private ArrayList<String> mDirectionDescriptions;
- private OnTriggerListener mOnTriggerListener;
- private TargetDrawable mHandleDrawable;
- private TargetDrawable mOuterRing;
- private Vibrator mVibrator;
-
- private int mFeedbackCount = 3;
- private int mVibrationDuration = 0;
- private int mGrabbedState;
- private int mActiveTarget = -1;
- private float mTapRadius;
- private float mWaveCenterX;
- private float mWaveCenterY;
- private int mMaxTargetHeight;
- private int mMaxTargetWidth;
-
- private float mOuterRadius = 0.0f;
- private float mSnapMargin = 0.0f;
- private boolean mDragging;
- private int mNewTargetResources;
-
- private class AnimationBundle extends ArrayList<Tweener> {
- private static final long serialVersionUID = 0xA84D78726F127468L;
- private boolean mSuspended;
-
- public void start() {
- if (mSuspended) return; // ignore attempts to start animations
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.start();
- }
- }
-
- public void cancel() {
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.cancel();
- }
- clear();
- }
-
- public void stop() {
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.end();
- }
- clear();
- }
-
- public void setSuspended(boolean suspend) {
- mSuspended = suspend;
- }
- };
-
- private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
- dispatchOnFinishFinalAnimation();
- }
- };
-
- private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- ping();
- switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
- dispatchOnFinishFinalAnimation();
- }
- };
-
- private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() {
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidateGlobalRegion(mHandleDrawable);
- invalidate();
- }
- };
-
- private boolean mAnimatingTargets;
- private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- if (mNewTargetResources != 0) {
- internalSetTargetResources(mNewTargetResources);
- mNewTargetResources = 0;
- hideTargets(false, false);
- }
- mAnimatingTargets = false;
- }
- };
- private int mTargetResourceId;
- private int mTargetDescriptionsResourceId;
- private int mDirectionDescriptionsResourceId;
- private boolean mAlwaysTrackFinger;
- private int mHorizontalInset;
- private int mVerticalInset;
- private int mGravity = Gravity.TOP;
- private boolean mInitialLayout = true;
- private Tweener mBackgroundAnimator;
-
- public MultiWaveView(Context context) {
- this(context, null);
- }
-
- public MultiWaveView(Context context, AttributeSet attrs) {
- super(context, attrs);
- Resources res = context.getResources();
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiWaveView);
- mOuterRadius = a.getDimension(R.styleable.MultiWaveView_outerRadius, mOuterRadius);
- mSnapMargin = a.getDimension(R.styleable.MultiWaveView_snapMargin, mSnapMargin);
- mVibrationDuration = a.getInt(R.styleable.MultiWaveView_vibrationDuration,
- mVibrationDuration);
- mFeedbackCount = a.getInt(R.styleable.MultiWaveView_feedbackCount,
- mFeedbackCount);
- mHandleDrawable = new TargetDrawable(res,
- a.peekValue(R.styleable.MultiWaveView_handleDrawable).resourceId);
- mTapRadius = mHandleDrawable.getWidth()/2;
- mOuterRing = new TargetDrawable(res,
- a.peekValue(R.styleable.MultiWaveView_waveDrawable).resourceId);
- mAlwaysTrackFinger = a.getBoolean(R.styleable.MultiWaveView_alwaysTrackFinger, false);
-
- // Read array of chevron drawables
- TypedValue outValue = new TypedValue();
- if (a.getValue(R.styleable.MultiWaveView_chevronDrawables, outValue)) {
- ArrayList<TargetDrawable> chevrons = loadDrawableArray(outValue.resourceId);
- for (int i = 0; i < chevrons.size(); i++) {
- final TargetDrawable chevron = chevrons.get(i);
- for (int k = 0; k < mFeedbackCount; k++) {
- mChevronDrawables.add(chevron == null ? null : new TargetDrawable(chevron));
- }
- }
- }
-
- // Read array of target drawables
- if (a.getValue(R.styleable.MultiWaveView_targetDrawables, outValue)) {
- internalSetTargetResources(outValue.resourceId);
- }
- if (mTargetDrawables == null || mTargetDrawables.size() == 0) {
- throw new IllegalStateException("Must specify at least one target drawable");
- }
-
- // Read array of target descriptions
- if (a.getValue(R.styleable.MultiWaveView_targetDescriptions, outValue)) {
- final int resourceId = outValue.resourceId;
- if (resourceId == 0) {
- throw new IllegalStateException("Must specify target descriptions");
- }
- setTargetDescriptionsResourceId(resourceId);
- }
-
- // Read array of direction descriptions
- if (a.getValue(R.styleable.MultiWaveView_directionDescriptions, outValue)) {
- final int resourceId = outValue.resourceId;
- if (resourceId == 0) {
- throw new IllegalStateException("Must specify direction descriptions");
- }
- setDirectionDescriptionsResourceId(resourceId);
- }
-
- a.recycle();
-
- // Use gravity attribute from LinearLayout
- a = context.obtainStyledAttributes(attrs, android.R.styleable.LinearLayout);
- mGravity = a.getInt(android.R.styleable.LinearLayout_gravity, Gravity.TOP);
- a.recycle();
-
- setVibrateEnabled(mVibrationDuration > 0);
- assignDefaultsIfNeeded();
- }
-
- private void dump() {
- Log.v(TAG, "Outer Radius = " + mOuterRadius);
- Log.v(TAG, "SnapMargin = " + mSnapMargin);
- Log.v(TAG, "FeedbackCount = " + mFeedbackCount);
- Log.v(TAG, "VibrationDuration = " + mVibrationDuration);
- Log.v(TAG, "TapRadius = " + mTapRadius);
- Log.v(TAG, "WaveCenterX = " + mWaveCenterX);
- Log.v(TAG, "WaveCenterY = " + mWaveCenterY);
- }
-
- public void suspendAnimations() {
- mChevronAnimations.setSuspended(true);
- mTargetAnimations.setSuspended(true);
- mHandleAnimations.setSuspended(true);
- }
-
- public void resumeAnimations() {
- mChevronAnimations.setSuspended(false);
- mTargetAnimations.setSuspended(false);
- mHandleAnimations.setSuspended(false);
- mChevronAnimations.start();
- mTargetAnimations.start();
- mHandleAnimations.start();
- }
-
- @Override
- protected int getSuggestedMinimumWidth() {
- // View should be large enough to contain the background + handle and
- // target drawable on either edge.
- return (int) (Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) + mMaxTargetWidth);
- }
-
- @Override
- protected int getSuggestedMinimumHeight() {
- // View should be large enough to contain the unlock ring + target and
- // target drawable on either edge
- return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight);
- }
-
- private int resolveMeasured(int measureSpec, int desired)
- {
- int result = 0;
- int specSize = MeasureSpec.getSize(measureSpec);
- switch (MeasureSpec.getMode(measureSpec)) {
- case MeasureSpec.UNSPECIFIED:
- result = desired;
- break;
- case MeasureSpec.AT_MOST:
- result = Math.min(specSize, desired);
- break;
- case MeasureSpec.EXACTLY:
- default:
- result = specSize;
- }
- return result;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int minimumWidth = getSuggestedMinimumWidth();
- final int minimumHeight = getSuggestedMinimumHeight();
- int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
- int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
- computeInsets((computedWidth - minimumWidth), (computedHeight - minimumHeight));
- setMeasuredDimension(computedWidth, computedHeight);
- }
-
- private void switchToState(int state, float x, float y) {
- switch (state) {
- case STATE_IDLE:
- deactivateTargets();
- hideTargets(true, false);
- startBackgroundAnimation(0, 0.0f);
- mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
- break;
-
- case STATE_START:
- deactivateHandle(0, 0, 1.0f, null);
- startBackgroundAnimation(0, 0.0f);
- break;
-
- case STATE_FIRST_TOUCH:
- deactivateTargets();
- showTargets(true);
- mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE);
- startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f);
- setGrabbedState(OnTriggerListener.CENTER_HANDLE);
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- announceTargets();
- }
- break;
-
- case STATE_TRACKING:
- break;
-
- case STATE_SNAP:
- break;
-
- case STATE_FINISH:
- doFinish();
- break;
- }
- }
-
- private void activateHandle(int duration, int delay, float finalAlpha,
- AnimatorListener finishListener) {
- mHandleAnimations.cancel();
- mHandleAnimations.add(Tweener.to(mHandleDrawable, duration,
- "ease", Ease.Cubic.easeIn,
- "delay", delay,
- "alpha", finalAlpha,
- "onUpdate", mUpdateListener,
- "onComplete", finishListener));
- mHandleAnimations.start();
- }
-
- private void deactivateHandle(int duration, int delay, float finalAlpha,
- AnimatorListener finishListener) {
- mHandleAnimations.cancel();
- mHandleAnimations.add(Tweener.to(mHandleDrawable, duration,
- "ease", Ease.Quart.easeOut,
- "delay", delay,
- "alpha", finalAlpha,
- "x", 0,
- "y", 0,
- "onUpdate", mUpdateListener,
- "onComplete", finishListener));
- mHandleAnimations.start();
- }
-
- /**
- * Animation used to attract user's attention to the target button.
- * Assumes mChevronDrawables is an a list with an even number of chevrons filled with
- * mFeedbackCount items in the order: left, right, top, bottom.
- */
- private void startChevronAnimation() {
- final float chevronStartDistance = mHandleDrawable.getWidth() * 0.8f;
- final float chevronStopDistance = mOuterRadius * 0.9f / 2.0f;
- final float startScale = 0.5f;
- final float endScale = 2.0f;
- final int directionCount = mFeedbackCount > 0 ? mChevronDrawables.size()/mFeedbackCount : 0;
-
- mChevronAnimations.stop();
-
- // Add an animation for all chevron drawables. There are mFeedbackCount drawables
- // in each direction and directionCount directions.
- for (int direction = 0; direction < directionCount; direction++) {
- double angle = 2.0 * Math.PI * direction / directionCount;
- final float sx = (float) Math.cos(angle);
- final float sy = 0.0f - (float) Math.sin(angle);
- final float[] xrange = new float[]
- {sx * chevronStartDistance, sx * chevronStopDistance};
- final float[] yrange = new float[]
- {sy * chevronStartDistance, sy * chevronStopDistance};
- for (int count = 0; count < mFeedbackCount; count++) {
- int delay = count * CHEVRON_INCREMENTAL_DELAY;
- final TargetDrawable icon = mChevronDrawables.get(direction*mFeedbackCount + count);
- if (icon == null) {
- continue;
- }
- mChevronAnimations.add(Tweener.to(icon, CHEVRON_ANIMATION_DURATION,
- "ease", mChevronAnimationInterpolator,
- "delay", delay,
- "x", xrange,
- "y", yrange,
- "alpha", new float[] {1.0f, 0.0f},
- "scaleX", new float[] {startScale, endScale},
- "scaleY", new float[] {startScale, endScale},
- "onUpdate", mUpdateListener));
- }
- }
- mChevronAnimations.start();
- }
-
- private void deactivateTargets() {
- final int count = mTargetDrawables.size();
- for (int i = 0; i < count; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- }
- mActiveTarget = -1;
- }
-
- void invalidateGlobalRegion(TargetDrawable drawable) {
- int width = drawable.getWidth();
- int height = drawable.getHeight();
- RectF childBounds = new RectF(0, 0, width, height);
- childBounds.offset(drawable.getX() - width/2, drawable.getY() - height/2);
- View view = this;
- while (view.getParent() != null && view.getParent() instanceof View) {
- view = (View) view.getParent();
- view.getMatrix().mapRect(childBounds);
- view.invalidate((int) Math.floor(childBounds.left),
- (int) Math.floor(childBounds.top),
- (int) Math.ceil(childBounds.right),
- (int) Math.ceil(childBounds.bottom));
- }
- }
-
- /**
- * Dispatches a trigger event to listener. Ignored if a listener is not set.
- * @param whichTarget the target that was triggered.
- */
- private void dispatchTriggerEvent(int whichTarget) {
- vibrate();
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onTrigger(this, whichTarget);
- }
- }
-
- private void dispatchOnFinishFinalAnimation() {
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onFinishFinalAnimation();
- }
- }
-
- private void doFinish() {
- final int activeTarget = mActiveTarget;
- final boolean targetHit = activeTarget != -1;
-
- if (targetHit) {
- if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
-
- highlightSelected(activeTarget);
-
- // Inform listener of any active targets. Typically only one will be active.
- deactivateHandle(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener);
- dispatchTriggerEvent(activeTarget);
- if (!mAlwaysTrackFinger) {
- // Force ring and targets to finish animation to final expanded state
- mTargetAnimations.stop();
- }
- } else {
- // Animate handle back to the center based on current state.
- deactivateHandle(HIDE_ANIMATION_DURATION, HIDE_ANIMATION_DELAY, 1.0f,
- mResetListenerWithPing);
- hideTargets(true, false);
- }
-
- setGrabbedState(OnTriggerListener.NO_HANDLE);
- }
-
- private void highlightSelected(int activeTarget) {
- // Highlight the given target and fade others
- mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
- hideUnselected(activeTarget);
- }
-
- private void hideUnselected(int active) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- if (i != active) {
- mTargetDrawables.get(i).setAlpha(0.0f);
- }
- }
- }
-
- private void hideTargets(boolean animate, boolean expanded) {
- mTargetAnimations.cancel();
- // Note: these animations should complete at the same time so that we can swap out
- // the target assets asynchronously from the setTargetResources() call.
- mAnimatingTargets = animate;
- final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
- final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
-
- final float targetScale = expanded ? TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED;
- final int length = mTargetDrawables.size();
- for (int i = 0; i < length; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 0.0f,
- "scaleX", targetScale,
- "scaleY", targetScale,
- "delay", delay,
- "onUpdate", mUpdateListener));
- }
-
- final float ringScaleTarget = expanded ? RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED;
- mTargetAnimations.add(Tweener.to(mOuterRing, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 0.0f,
- "scaleX", ringScaleTarget,
- "scaleY", ringScaleTarget,
- "delay", delay,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
-
- mTargetAnimations.start();
- }
-
- private void showTargets(boolean animate) {
- mTargetAnimations.stop();
- mAnimatingTargets = animate;
- final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
- final int duration = animate ? SHOW_ANIMATION_DURATION : 0;
- final int length = mTargetDrawables.size();
- for (int i = 0; i < length; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 1.0f,
- "scaleX", 1.0f,
- "scaleY", 1.0f,
- "delay", delay,
- "onUpdate", mUpdateListener));
- }
- mTargetAnimations.add(Tweener.to(mOuterRing, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 1.0f,
- "scaleX", 1.0f,
- "scaleY", 1.0f,
- "delay", delay,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
-
- mTargetAnimations.start();
- }
-
- private void vibrate() {
- final boolean hapticEnabled = Settings.System.getIntForUser(
- mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
- UserHandle.USER_CURRENT) != 0;
- if (mVibrator != null && hapticEnabled) {
- mVibrator.vibrate(mVibrationDuration);
- }
- }
-
- private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) {
- Resources res = getContext().getResources();
- TypedArray array = res.obtainTypedArray(resourceId);
- final int count = array.length();
- ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count);
- for (int i = 0; i < count; i++) {
- TypedValue value = array.peekValue(i);
- TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0);
- drawables.add(target);
- }
- array.recycle();
- return drawables;
- }
-
- private void internalSetTargetResources(int resourceId) {
- mTargetDrawables = loadDrawableArray(resourceId);
- mTargetResourceId = resourceId;
- final int count = mTargetDrawables.size();
- int maxWidth = mHandleDrawable.getWidth();
- int maxHeight = mHandleDrawable.getHeight();
- for (int i = 0; i < count; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- maxWidth = Math.max(maxWidth, target.getWidth());
- maxHeight = Math.max(maxHeight, target.getHeight());
- }
- if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) {
- mMaxTargetWidth = maxWidth;
- mMaxTargetHeight = maxHeight;
- requestLayout(); // required to resize layout and call updateTargetPositions()
- } else {
- updateTargetPositions(mWaveCenterX, mWaveCenterY);
- updateChevronPositions(mWaveCenterX, mWaveCenterY);
- }
- }
-
- /**
- * Loads an array of drawables from the given resourceId.
- *
- * @param resourceId
- */
- public void setTargetResources(int resourceId) {
- if (mAnimatingTargets) {
- // postpone this change until we return to the initial state
- mNewTargetResources = resourceId;
- } else {
- internalSetTargetResources(resourceId);
- }
- }
-
- public int getTargetResourceId() {
- return mTargetResourceId;
- }
-
- /**
- * Sets the resource id specifying the target descriptions for accessibility.
- *
- * @param resourceId The resource id.
- */
- public void setTargetDescriptionsResourceId(int resourceId) {
- mTargetDescriptionsResourceId = resourceId;
- if (mTargetDescriptions != null) {
- mTargetDescriptions.clear();
- }
- }
-
- /**
- * Gets the resource id specifying the target descriptions for accessibility.
- *
- * @return The resource id.
- */
- public int getTargetDescriptionsResourceId() {
- return mTargetDescriptionsResourceId;
- }
-
- /**
- * Sets the resource id specifying the target direction descriptions for accessibility.
- *
- * @param resourceId The resource id.
- */
- public void setDirectionDescriptionsResourceId(int resourceId) {
- mDirectionDescriptionsResourceId = resourceId;
- if (mDirectionDescriptions != null) {
- mDirectionDescriptions.clear();
- }
- }
-
- /**
- * Gets the resource id specifying the target direction descriptions.
- *
- * @return The resource id.
- */
- public int getDirectionDescriptionsResourceId() {
- return mDirectionDescriptionsResourceId;
- }
-
- /**
- * Enable or disable vibrate on touch.
- *
- * @param enabled
- */
- public void setVibrateEnabled(boolean enabled) {
- if (enabled && mVibrator == null) {
- mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
- } else {
- mVibrator = null;
- }
- }
-
- /**
- * Starts chevron animation. Example use case: show chevron animation whenever the phone rings
- * or the user touches the screen.
- *
- */
- public void ping() {
- startChevronAnimation();
- }
-
- /**
- * Resets the widget to default state and cancels all animation. If animate is 'true', will
- * animate objects into place. Otherwise, objects will snap back to place.
- *
- * @param animate
- */
- public void reset(boolean animate) {
- mChevronAnimations.stop();
- mHandleAnimations.stop();
- mTargetAnimations.stop();
- startBackgroundAnimation(0, 0.0f);
- hideChevrons();
- hideTargets(animate, false);
- deactivateHandle(0, 0, 1.0f, null);
- Tweener.reset();
- }
-
- private void startBackgroundAnimation(int duration, float alpha) {
- Drawable background = getBackground();
- if (mAlwaysTrackFinger && background != null) {
- if (mBackgroundAnimator != null) {
- mBackgroundAnimator.animator.end();
- }
- mBackgroundAnimator = Tweener.to(background, duration,
- "ease", Ease.Cubic.easeIn,
- "alpha", new int[] {0, (int)(255.0f * alpha)},
- "delay", SHOW_ANIMATION_DELAY);
- mBackgroundAnimator.animator.start();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- final int action = event.getAction();
- boolean handled = false;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- if (DEBUG) Log.v(TAG, "*** DOWN ***");
- handleDown(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (DEBUG) Log.v(TAG, "*** MOVE ***");
- handleMove(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_UP:
- if (DEBUG) Log.v(TAG, "*** UP ***");
- handleMove(event);
- handleUp(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (DEBUG) Log.v(TAG, "*** CANCEL ***");
- handleMove(event);
- handleCancel(event);
- handled = true;
- break;
- }
- invalidate();
- return handled ? true : super.onTouchEvent(event);
- }
-
- private void moveHandleTo(float x, float y, boolean animate) {
- mHandleDrawable.setX(x);
- mHandleDrawable.setY(y);
- }
-
- private void handleDown(MotionEvent event) {
- float eventX = event.getX();
- float eventY = event.getY();
- switchToState(STATE_START, eventX, eventY);
- if (!trySwitchToFirstTouchState(eventX, eventY)) {
- mDragging = false;
- ping();
- }
- }
-
- private void handleUp(MotionEvent event) {
- if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
- switchToState(STATE_FINISH, event.getX(), event.getY());
- }
-
- private void handleCancel(MotionEvent event) {
- if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
-
- // We should drop the active target here but it interferes with
- // moving off the screen in the direction of the navigation bar. At some point we may
- // want to revisit how we handle this. For now we'll allow a canceled event to
- // activate the current target.
-
- // mActiveTarget = -1; // Drop the active target if canceled.
-
- switchToState(STATE_FINISH, event.getX(), event.getY());
- }
-
- private void handleMove(MotionEvent event) {
- int activeTarget = -1;
- final int historySize = event.getHistorySize();
- ArrayList<TargetDrawable> targets = mTargetDrawables;
- int ntargets = targets.size();
- float x = 0.0f;
- float y = 0.0f;
- for (int k = 0; k < historySize + 1; k++) {
- float eventX = k < historySize ? event.getHistoricalX(k) : event.getX();
- float eventY = k < historySize ? event.getHistoricalY(k) : event.getY();
- // tx and ty are relative to wave center
- float tx = eventX - mWaveCenterX;
- float ty = eventY - mWaveCenterY;
- float touchRadius = (float) Math.sqrt(dist2(tx, ty));
- final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f;
- float limitX = tx * scale;
- float limitY = ty * scale;
- double angleRad = Math.atan2(-ty, tx);
-
- if (!mDragging) {
- trySwitchToFirstTouchState(eventX, eventY);
- }
-
- if (mDragging) {
- // For multiple targets, snap to the one that matches
- final float snapRadius = mOuterRadius - mSnapMargin;
- final float snapDistance2 = snapRadius * snapRadius;
- // Find first target in range
- for (int i = 0; i < ntargets; i++) {
- TargetDrawable target = targets.get(i);
-
- double targetMinRad = (i - 0.5) * 2 * Math.PI / ntargets;
- double targetMaxRad = (i + 0.5) * 2 * Math.PI / ntargets;
- if (target.isEnabled()) {
- boolean angleMatches =
- (angleRad > targetMinRad && angleRad <= targetMaxRad) ||
- (angleRad + 2 * Math.PI > targetMinRad &&
- angleRad + 2 * Math.PI <= targetMaxRad);
- if (angleMatches && (dist2(tx, ty) > snapDistance2)) {
- activeTarget = i;
- }
- }
- }
- }
- x = limitX;
- y = limitY;
- }
-
- if (!mDragging) {
- return;
- }
-
- if (activeTarget != -1) {
- switchToState(STATE_SNAP, x,y);
- moveHandleTo(x, y, false);
- } else {
- switchToState(STATE_TRACKING, x, y);
- moveHandleTo(x, y, false);
- }
-
- // Draw handle outside parent's bounds
- invalidateGlobalRegion(mHandleDrawable);
-
- if (mActiveTarget != activeTarget) {
- // Defocus the old target
- if (mActiveTarget != -1) {
- TargetDrawable target = targets.get(mActiveTarget);
- if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
- target.setState(TargetDrawable.STATE_INACTIVE);
- }
- }
- // Focus the new target
- if (activeTarget != -1) {
- TargetDrawable target = targets.get(activeTarget);
- if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
- target.setState(TargetDrawable.STATE_FOCUSED);
- }
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- String targetContentDescription = getTargetDescription(activeTarget);
- announceText(targetContentDescription);
- }
- activateHandle(0, 0, 0.0f, null);
- } else {
- activateHandle(0, 0, 1.0f, null);
- }
- }
- mActiveTarget = activeTarget;
- }
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_HOVER_ENTER:
- event.setAction(MotionEvent.ACTION_DOWN);
- break;
- case MotionEvent.ACTION_HOVER_MOVE:
- event.setAction(MotionEvent.ACTION_MOVE);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- event.setAction(MotionEvent.ACTION_UP);
- break;
- }
- onTouchEvent(event);
- event.setAction(action);
- }
- return super.onHoverEvent(event);
- }
-
- /**
- * Sets the current grabbed state, and dispatches a grabbed state change
- * event to our listener.
- */
- private void setGrabbedState(int newState) {
- if (newState != mGrabbedState) {
- if (newState != OnTriggerListener.NO_HANDLE) {
- vibrate();
- }
- mGrabbedState = newState;
- if (mOnTriggerListener != null) {
- if (newState == OnTriggerListener.NO_HANDLE) {
- mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE);
- } else {
- mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE);
- }
- mOnTriggerListener.onGrabbedStateChange(this, newState);
- }
- }
- }
-
- private boolean trySwitchToFirstTouchState(float x, float y) {
- final float tx = x - mWaveCenterX;
- final float ty = y - mWaveCenterY;
- if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledTapRadiusSquared()) {
- if (DEBUG) Log.v(TAG, "** Handle HIT");
- switchToState(STATE_FIRST_TOUCH, x, y);
- moveHandleTo(tx, ty, false);
- mDragging = true;
- return true;
- }
- return false;
- }
-
- private void assignDefaultsIfNeeded() {
- if (mOuterRadius == 0.0f) {
- mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f;
- }
- if (mSnapMargin == 0.0f) {
- mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics());
- }
- }
-
- private void computeInsets(int dx, int dy) {
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- mHorizontalInset = 0;
- break;
- case Gravity.RIGHT:
- mHorizontalInset = dx;
- break;
- case Gravity.CENTER_HORIZONTAL:
- default:
- mHorizontalInset = dx / 2;
- break;
- }
- switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.TOP:
- mVerticalInset = 0;
- break;
- case Gravity.BOTTOM:
- mVerticalInset = dy;
- break;
- case Gravity.CENTER_VERTICAL:
- default:
- mVerticalInset = dy / 2;
- break;
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- final int width = right - left;
- final int height = bottom - top;
-
- // Target placement width/height. This puts the targets on the greater of the ring
- // width or the specified outer radius.
- final float placementWidth = Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
- final float placementHeight = Math.max(mOuterRing.getHeight(), 2 * mOuterRadius);
- float newWaveCenterX = mHorizontalInset
- + Math.max(width, mMaxTargetWidth + placementWidth) / 2;
- float newWaveCenterY = mVerticalInset
- + Math.max(height, + mMaxTargetHeight + placementHeight) / 2;
-
- if (mInitialLayout) {
- hideChevrons();
- hideTargets(false, false);
- moveHandleTo(0, 0, false);
- mInitialLayout = false;
- }
-
- mOuterRing.setPositionX(newWaveCenterX);
- mOuterRing.setPositionY(newWaveCenterY);
-
- mHandleDrawable.setPositionX(newWaveCenterX);
- mHandleDrawable.setPositionY(newWaveCenterY);
-
- updateTargetPositions(newWaveCenterX, newWaveCenterY);
- updateChevronPositions(newWaveCenterX, newWaveCenterY);
-
- mWaveCenterX = newWaveCenterX;
- mWaveCenterY = newWaveCenterY;
-
- if (DEBUG) dump();
- }
-
- private void updateTargetPositions(float centerX, float centerY) {
- // Reposition the target drawables if the view changed.
- ArrayList<TargetDrawable> targets = mTargetDrawables;
- final int size = targets.size();
- final float alpha = (float) (-2.0f * Math.PI / size);
- for (int i = 0; i < size; i++) {
- final TargetDrawable targetIcon = targets.get(i);
- final float angle = alpha * i;
- targetIcon.setPositionX(centerX);
- targetIcon.setPositionY(centerY);
- targetIcon.setX(mOuterRadius * (float) Math.cos(angle));
- targetIcon.setY(mOuterRadius * (float) Math.sin(angle));
- }
- }
-
- private void updateChevronPositions(float centerX, float centerY) {
- ArrayList<TargetDrawable> chevrons = mChevronDrawables;
- final int size = chevrons.size();
- for (int i = 0; i < size; i++) {
- TargetDrawable target = chevrons.get(i);
- if (target != null) {
- target.setPositionX(centerX);
- target.setPositionY(centerY);
- }
- }
- }
-
- private void hideChevrons() {
- ArrayList<TargetDrawable> chevrons = mChevronDrawables;
- final int size = chevrons.size();
- for (int i = 0; i < size; i++) {
- TargetDrawable chevron = chevrons.get(i);
- if (chevron != null) {
- chevron.setAlpha(0.0f);
- }
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- mOuterRing.draw(canvas);
- final int ntargets = mTargetDrawables.size();
- for (int i = 0; i < ntargets; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- if (target != null) {
- target.draw(canvas);
- }
- }
- final int nchevrons = mChevronDrawables.size();
- for (int i = 0; i < nchevrons; i++) {
- TargetDrawable chevron = mChevronDrawables.get(i);
- if (chevron != null) {
- chevron.draw(canvas);
- }
- }
- mHandleDrawable.draw(canvas);
- }
-
- public void setOnTriggerListener(OnTriggerListener listener) {
- mOnTriggerListener = listener;
- }
-
- private float square(float d) {
- return d * d;
- }
-
- private float dist2(float dx, float dy) {
- return dx*dx + dy*dy;
- }
-
- private float getScaledTapRadiusSquared() {
- final float scaledTapRadius;
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- scaledTapRadius = TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mTapRadius;
- } else {
- scaledTapRadius = mTapRadius;
- }
- return square(scaledTapRadius);
- }
-
- private void announceTargets() {
- StringBuilder utterance = new StringBuilder();
- final int targetCount = mTargetDrawables.size();
- for (int i = 0; i < targetCount; i++) {
- String targetDescription = getTargetDescription(i);
- String directionDescription = getDirectionDescription(i);
- if (!TextUtils.isEmpty(targetDescription)
- && !TextUtils.isEmpty(directionDescription)) {
- String text = String.format(directionDescription, targetDescription);
- utterance.append(text);
- }
- if (utterance.length() > 0) {
- announceText(utterance.toString());
- }
- }
- }
-
- private void announceText(String text) {
- setContentDescription(text);
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
- setContentDescription(null);
- }
-
- private String getTargetDescription(int index) {
- if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) {
- mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId);
- if (mTargetDrawables.size() != mTargetDescriptions.size()) {
- Log.w(TAG, "The number of target drawables must be"
- + " euqal to the number of target descriptions.");
- return null;
- }
- }
- return mTargetDescriptions.get(index);
- }
-
- private String getDirectionDescription(int index) {
- if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) {
- mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId);
- if (mTargetDrawables.size() != mDirectionDescriptions.size()) {
- Log.w(TAG, "The number of target drawables must be"
- + " euqal to the number of direction descriptions.");
- return null;
- }
- }
- return mDirectionDescriptions.get(index);
- }
-
- private ArrayList<String> loadDescriptions(int resourceId) {
- TypedArray array = getContext().getResources().obtainTypedArray(resourceId);
- final int count = array.length();
- ArrayList<String> targetContentDescriptions = new ArrayList<String>(count);
- for (int i = 0; i < count; i++) {
- String contentDescription = array.getString(i);
- targetContentDescriptions.add(contentDescription);
- }
- array.recycle();
- return targetContentDescriptions;
- }
-
- public int getResourceIdForTarget(int index) {
- final TargetDrawable drawable = mTargetDrawables.get(index);
- return drawable == null ? 0 : drawable.getResourceId();
- }
-
- public void setEnableTarget(int resourceId, boolean enabled) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- final TargetDrawable target = mTargetDrawables.get(i);
- if (target.getResourceId() == resourceId) {
- target.setEnabled(enabled);
- break; // should never be more than one match
- }
- }
- }
-
- /**
- * Gets the position of a target in the array that matches the given resource.
- * @param resourceId
- * @return the index or -1 if not found
- */
- public int getTargetPosition(int resourceId) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- final TargetDrawable target = mTargetDrawables.get(i);
- if (target.getResourceId() == resourceId) {
- return i; // should never be more than one match
- }
- }
- return -1;
- }
-
- private boolean replaceTargetDrawables(Resources res, int existingResourceId,
- int newResourceId) {
- if (existingResourceId == 0 || newResourceId == 0) {
- return false;
- }
-
- boolean result = false;
- final ArrayList<TargetDrawable> drawables = mTargetDrawables;
- final int size = drawables.size();
- for (int i = 0; i < size; i++) {
- final TargetDrawable target = drawables.get(i);
- if (target != null && target.getResourceId() == existingResourceId) {
- target.setDrawable(res, newResourceId);
- result = true;
- }
- }
-
- if (result) {
- requestLayout(); // in case any given drawable's size changes
- }
-
- return result;
- }
-
- /**
- * Searches the given package for a resource to use to replace the Drawable on the
- * target with the given resource id
- * @param component of the .apk that contains the resource
- * @param name of the metadata in the .apk
- * @param existingResId the resource id of the target to search for
- * @return true if found in the given package and replaced at least one target Drawables
- */
- public boolean replaceTargetDrawablesIfPresent(ComponentName component, String name,
- int existingResId) {
- if (existingResId == 0) return false;
-
- try {
- PackageManager packageManager = mContext.getPackageManager();
- // Look for the search icon specified in the activity meta-data
- Bundle metaData = packageManager.getActivityInfo(
- component, PackageManager.GET_META_DATA).metaData;
- if (metaData != null) {
- int iconResId = metaData.getInt(name);
- if (iconResId != 0) {
- Resources res = packageManager.getResourcesForActivity(component);
- return replaceTargetDrawables(res, existingResId, iconResId);
- }
- }
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Failed to swap drawable; "
- + component.flattenToShortString() + " not found", e);
- } catch (Resources.NotFoundException nfe) {
- Log.w(TAG, "Failed to swap drawable from "
- + component.flattenToShortString(), nfe);
- }
- return false;
- }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
index 16bec16..5a4c441 100644
--- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
+++ b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
@@ -18,7 +18,6 @@ package com.android.internal.widget.multiwaveview;
import android.content.res.Resources;
import android.graphics.Canvas;
-import android.graphics.ColorFilter;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.util.Log;
diff --git a/core/java/com/android/server/AppWidgetBackupBridge.java b/core/java/com/android/server/AppWidgetBackupBridge.java
new file mode 100644
index 0000000..2ea2f79
--- /dev/null
+++ b/core/java/com/android/server/AppWidgetBackupBridge.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import java.util.List;
+
+/**
+ * Runtime bridge between the Backup Manager Service and the App Widget Service,
+ * since those two modules are intentionally decoupled for modularity.
+ *
+ * @hide
+ */
+public class AppWidgetBackupBridge {
+ private static WidgetBackupProvider sAppWidgetService;
+
+ public static void register(WidgetBackupProvider instance) {
+ sAppWidgetService = instance;
+ }
+
+ public static List<String> getWidgetParticipants(int userId) {
+ return (sAppWidgetService != null)
+ ? sAppWidgetService.getWidgetParticipants(userId)
+ : null;
+ }
+
+ public static byte[] getWidgetState(String packageName, int userId) {
+ return (sAppWidgetService != null)
+ ? sAppWidgetService.getWidgetState(packageName, userId)
+ : null;
+ }
+
+ public static void restoreStarting(int userId) {
+ if (sAppWidgetService != null) {
+ sAppWidgetService.restoreStarting(userId);
+ }
+ }
+
+ public static void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+ if (sAppWidgetService != null) {
+ sAppWidgetService.restoreWidgetState(packageName, restoredState, userId);
+ }
+ }
+
+ public static void restoreFinished(int userId) {
+ if (sAppWidgetService != null) {
+ sAppWidgetService.restoreFinished(userId);
+ }
+ }
+}
diff --git a/core/java/com/android/server/SystemService.java b/core/java/com/android/server/SystemService.java
index e374563..bf36bb1 100644
--- a/core/java/com/android/server/SystemService.java
+++ b/core/java/com/android/server/SystemService.java
@@ -123,6 +123,38 @@ public abstract class SystemService {
public void onBootPhase(int phase) {}
/**
+ * Called when a new user is starting, for system services to initialize any per-user
+ * state they maintain for running users.
+ * @param userHandle The identifier of the user.
+ */
+ public void onStartUser(int userHandle) {}
+
+ /**
+ * Called when switching to a different foreground user, for system services that have
+ * special behavior for whichever user is currently in the foreground. This is called
+ * before any application processes are aware of the new user.
+ * @param userHandle The identifier of the user.
+ */
+ public void onSwitchUser(int userHandle) {}
+
+ /**
+ * Called when an existing user is stopping, for system services to finalize any per-user
+ * state they maintain for running users. This is called prior to sending the SHUTDOWN
+ * broadcast to the user; it is a good place to stop making use of any resources of that
+ * user (such as binding to a service running in the user).
+ * @param userHandle The identifier of the user.
+ */
+ public void onStopUser(int userHandle) {}
+
+ /**
+ * Called when an existing user is stopping, for system services to finalize any per-user
+ * state they maintain for running users. This is called after all application process
+ * teardown of the user is complete.
+ * @param userHandle The identifier of the user.
+ */
+ public void onCleanupUser(int userHandle) {}
+
+ /**
* Publish the service so it is accessible to other services and apps.
*/
protected final void publishBinderService(String name, IBinder service) {
diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java
index eb8df0e..87a50a9 100644
--- a/core/java/com/android/server/SystemServiceManager.java
+++ b/core/java/com/android/server/SystemServiceManager.java
@@ -131,6 +131,58 @@ public class SystemServiceManager {
}
}
+ public void startUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onStartUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting start of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ public void switchUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onSwitchUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ public void stopUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onStopUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ public void cleanupUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onCleanupUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
/** Sets the safe mode flag for services to query. */
public void setSafeMode(boolean safeMode) {
mSafeMode = safeMode;
diff --git a/core/java/com/android/server/WidgetBackupProvider.java b/core/java/com/android/server/WidgetBackupProvider.java
new file mode 100644
index 0000000..a2efbdd
--- /dev/null
+++ b/core/java/com/android/server/WidgetBackupProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import java.util.List;
+
+/**
+ * Shim to allow core/backup to communicate with the app widget service
+ * about various important events without needing to be able to see the
+ * implementation of the service.
+ *
+ * @hide
+ */
+public interface WidgetBackupProvider {
+ public List<String> getWidgetParticipants(int userId);
+ public byte[] getWidgetState(String packageName, int userId);
+ public void restoreStarting(int userId);
+ public void restoreWidgetState(String packageName, byte[] restoredState, int userId);
+ public void restoreFinished(int userId);
+}
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 5502a17..430dd63 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -57,7 +57,7 @@ public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub {
}
@Override
- public void interfaceClassDataActivityChanged(String label, boolean active) {
+ public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos) {
// default no-op
}