Merge "Add mcc/mnc to SubInfoRecord" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 55a3efa..7e2b0ed 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -260,6 +260,7 @@
public static final class R.attr {
ctor public R.attr();
+ field public static final int __removed2 = 16843937; // 0x10104a1
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -482,7 +483,7 @@
field public static final int datePickerMode = 16843956; // 0x10104b4
field public static final int datePickerStyle = 16843612; // 0x101035c
field public static final int dateTextAppearance = 16843593; // 0x1010349
- field public static final int dayOfWeekBackgroundColor = 16843924; // 0x1010494
+ field public static final int dayOfWeekBackground = 16843924; // 0x1010494
field public static final int dayOfWeekTextAppearance = 16843925; // 0x1010495
field public static final int debuggable = 16842767; // 0x101000f
field public static final int defaultValue = 16843245; // 0x10101ed
@@ -663,7 +664,6 @@
field public static final int hasCode = 16842764; // 0x101000c
field public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
field public static final int headerBackground = 16843055; // 0x101012f
- field public static final int headerBackgroundColor = 16843937; // 0x10104a1
field public static final int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
field public static final int headerDividersEnabled = 16843310; // 0x101022e
field public static final int headerMonthTextAppearance = 16843926; // 0x1010496
@@ -5706,6 +5706,19 @@
package android.app.usage {
+ public final class ConfigurationStats implements android.os.Parcelable {
+ ctor public ConfigurationStats(android.app.usage.ConfigurationStats);
+ method public int describeContents();
+ method public int getActivationCount();
+ method public android.content.res.Configuration getConfiguration();
+ method public long getFirstTimeStamp();
+ method public long getLastTimeActive();
+ method public long getLastTimeStamp();
+ method public long getTotalTimeActive();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public final class UsageEvents implements android.os.Parcelable {
method public int describeContents();
method public boolean getNextEvent(android.app.usage.UsageEvents.Event);
@@ -5718,9 +5731,11 @@
public static final class UsageEvents.Event {
ctor public UsageEvents.Event();
method public java.lang.String getClassName();
+ method public android.content.res.Configuration getConfiguration();
method public int getEventType();
method public java.lang.String getPackageName();
method public long getTimeStamp();
+ field public static final int CONFIGURATION_CHANGE = 5; // 0x5
field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
field public static final int NONE = 0; // 0x0
@@ -5741,6 +5756,7 @@
public final class UsageStatsManager {
method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
+ method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
field public static final int INTERVAL_BEST = 4; // 0x4
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index a64e0ed..2c596e5 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
@@ -385,6 +386,7 @@
}
/** @hide */
+ @SystemApi
public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
PendingIntent operation, WorkSource workSource) {
setImpl(type, triggerAtMillis, windowMillis, intervalMillis, operation, workSource, null);
diff --git a/core/java/android/app/usage/ConfigurationStats.java b/core/java/android/app/usage/ConfigurationStats.java
new file mode 100644
index 0000000..080216c
--- /dev/null
+++ b/core/java/android/app/usage/ConfigurationStats.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package android.app.usage;
+
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents the usage statistics of a device {@link android.content.res.Configuration} for a
+ * specific time range.
+ */
+public final class ConfigurationStats implements Parcelable {
+
+ /**
+ * {@hide}
+ */
+ public Configuration mConfiguration;
+
+ /**
+ * {@hide}
+ */
+ public long mBeginTimeStamp;
+
+ /**
+ * {@hide}
+ */
+ public long mEndTimeStamp;
+
+ /**
+ * {@hide}
+ */
+ public long mLastTimeActive;
+
+ /**
+ * {@hide}
+ */
+ public long mTotalTimeActive;
+
+ /**
+ * {@hide}
+ */
+ public int mActivationCount;
+
+ /**
+ * {@hide}
+ */
+ public ConfigurationStats() {
+ }
+
+ public ConfigurationStats(ConfigurationStats stats) {
+ mConfiguration = stats.mConfiguration;
+ mBeginTimeStamp = stats.mBeginTimeStamp;
+ mEndTimeStamp = stats.mEndTimeStamp;
+ mLastTimeActive = stats.mLastTimeActive;
+ mTotalTimeActive = stats.mTotalTimeActive;
+ mActivationCount = stats.mActivationCount;
+ }
+
+ public Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ /**
+ * Get the beginning of the time range this {@link ConfigurationStats} represents,
+ * measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
+ */
+ public long getFirstTimeStamp() {
+ return mBeginTimeStamp;
+ }
+
+ /**
+ * Get the end of the time range this {@link ConfigurationStats} represents,
+ * measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
+ */
+ public long getLastTimeStamp() {
+ return mEndTimeStamp;
+ }
+
+ /**
+ * Get the last time this configuration was active, measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
+ */
+ public long getLastTimeActive() {
+ return mLastTimeActive;
+ }
+
+ /**
+ * Get the total time this configuration was active, measured in milliseconds.
+ */
+ public long getTotalTimeActive() {
+ return mTotalTimeActive;
+ }
+
+ /**
+ * Get the number of times this configuration was active.
+ */
+ public int getActivationCount() {
+ return mActivationCount;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mConfiguration != null) {
+ dest.writeInt(1);
+ mConfiguration.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+
+ dest.writeLong(mBeginTimeStamp);
+ dest.writeLong(mEndTimeStamp);
+ dest.writeLong(mLastTimeActive);
+ dest.writeLong(mTotalTimeActive);
+ dest.writeInt(mActivationCount);
+ }
+
+ public static final Creator<ConfigurationStats> CREATOR = new Creator<ConfigurationStats>() {
+ @Override
+ public ConfigurationStats createFromParcel(Parcel source) {
+ ConfigurationStats stats = new ConfigurationStats();
+ if (source.readInt() != 0) {
+ stats.mConfiguration = Configuration.CREATOR.createFromParcel(source);
+ }
+ stats.mBeginTimeStamp = source.readLong();
+ stats.mEndTimeStamp = source.readLong();
+ stats.mLastTimeActive = source.readLong();
+ stats.mTotalTimeActive = source.readLong();
+ stats.mActivationCount = source.readInt();
+ return stats;
+ }
+
+ @Override
+ public ConfigurationStats[] newArray(int size) {
+ return new ConfigurationStats[size];
+ }
+ };
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 3b09888..4ed1489 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -27,5 +27,7 @@
interface IUsageStatsManager {
ParceledListSlice queryUsageStats(int bucketType, long beginTime, long endTime,
String callingPackage);
+ ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
+ String callingPackage);
UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index fb80de2..1a947ec 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,6 +16,7 @@
package android.app.usage;
import android.content.ComponentName;
+import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -63,6 +64,11 @@
public static final int CONTINUE_PREVIOUS_DAY = 4;
/**
+ * An event type denoting that the device configuration has changed.
+ */
+ public static final int CONFIGURATION_CHANGE = 5;
+
+ /**
* {@hide}
*/
public String mPackage;
@@ -83,6 +89,12 @@
public int mEventType;
/**
+ * Only present for {@link #CONFIGURATION_CHANGE} event types.
+ * {@hide}
+ */
+ public Configuration mConfiguration;
+
+ /**
* TODO(adamlesinski): Removed before release.
* {@hide}
*/
@@ -123,6 +135,14 @@
public int getEventType() {
return mEventType;
}
+
+ /**
+ * Returns a {@link Configuration} for this event if the event is of type
+ * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
+ */
+ public Configuration getConfiguration() {
+ return mConfiguration;
+ }
}
// Only used when creating the resulting events. Not used for reading/unparceling.
@@ -201,23 +221,9 @@
return false;
}
- final int packageIndex = mParcel.readInt();
- if (packageIndex >= 0) {
- eventOut.mPackage = mStringPool[packageIndex];
- } else {
- eventOut.mPackage = null;
- }
+ readEventFromParcel(mParcel, eventOut);
- final int classIndex = mParcel.readInt();
- if (classIndex >= 0) {
- eventOut.mClass = mStringPool[classIndex];
- } else {
- eventOut.mClass = null;
- }
- eventOut.mEventType = mParcel.readInt();
- eventOut.mTimeStamp = mParcel.readLong();
mIndex++;
-
if (mIndex >= mEventCount) {
mParcel.recycle();
mParcel = null;
@@ -235,11 +241,6 @@
}
}
- @Override
- public int describeContents() {
- return 0;
- }
-
private int findStringIndex(String str) {
final int index = Arrays.binarySearch(mStringPool, str);
if (index < 0) {
@@ -248,6 +249,66 @@
return index;
}
+ /**
+ * Writes a single event to the parcel. Modify this when updating {@link Event}.
+ */
+ private void writeEventToParcel(Event event, Parcel p, int flags) {
+ final int packageIndex;
+ if (event.mPackage != null) {
+ packageIndex = findStringIndex(event.mPackage);
+ } else {
+ packageIndex = -1;
+ }
+
+ final int classIndex;
+ if (event.mClass != null) {
+ classIndex = findStringIndex(event.mClass);
+ } else {
+ classIndex = -1;
+ }
+ p.writeInt(packageIndex);
+ p.writeInt(classIndex);
+ p.writeInt(event.mEventType);
+ p.writeLong(event.mTimeStamp);
+
+ if (event.mEventType == Event.CONFIGURATION_CHANGE) {
+ event.mConfiguration.writeToParcel(p, flags);
+ }
+ }
+
+ /**
+ * Reads a single event from the parcel. Modify this when updating {@link Event}.
+ */
+ private void readEventFromParcel(Parcel p, Event eventOut) {
+ final int packageIndex = p.readInt();
+ if (packageIndex >= 0) {
+ eventOut.mPackage = mStringPool[packageIndex];
+ } else {
+ eventOut.mPackage = null;
+ }
+
+ final int classIndex = p.readInt();
+ if (classIndex >= 0) {
+ eventOut.mClass = mStringPool[classIndex];
+ } else {
+ eventOut.mClass = null;
+ }
+ eventOut.mEventType = p.readInt();
+ eventOut.mTimeStamp = p.readLong();
+
+ // Extract the configuration for configuration change events.
+ if (eventOut.mEventType == Event.CONFIGURATION_CHANGE) {
+ eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
+ } else {
+ eventOut.mConfiguration = null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mEventCount);
@@ -262,25 +323,9 @@
p.setDataPosition(0);
for (int i = 0; i < mEventCount; i++) {
final Event event = mEventsToWrite.get(i);
-
- final int packageIndex;
- if (event.mPackage != null) {
- packageIndex = findStringIndex(event.mPackage);
- } else {
- packageIndex = -1;
- }
-
- final int classIndex;
- if (event.mClass != null) {
- classIndex = findStringIndex(event.mClass);
- } else {
- classIndex = -1;
- }
- p.writeInt(packageIndex);
- p.writeInt(classIndex);
- p.writeInt(event.getEventType());
- p.writeLong(event.getTimeStamp());
+ writeEventToParcel(event, p, flags);
}
+
final int listByteLength = p.dataPosition();
// Write the total length of the data.
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5830fcf..bc6099a 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -125,9 +125,9 @@
* @see #INTERVAL_YEARLY
* @see #INTERVAL_BEST
*/
- @SuppressWarnings("unchecked")
public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
try {
+ @SuppressWarnings("unchecked")
ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
endTime, mContext.getOpPackageName());
if (slice != null) {
@@ -136,7 +136,32 @@
} catch (RemoteException e) {
// fallthrough and return null.
}
- return Collections.EMPTY_LIST;
+ return Collections.emptyList();
+ }
+
+ /**
+ * Gets the hardware configurations the device was in for the given time range, aggregated by
+ * the specified interval. The results are ordered as in
+ * {@link #queryUsageStats(int, long, long)}.
+ *
+ * @param intervalType The time interval by which the stats are aggregated.
+ * @param beginTime The inclusive beginning of the range of stats to include in the results.
+ * @param endTime The exclusive end of the range of stats to include in the results.
+ * @return A list of {@link ConfigurationStats} or null if none are available.
+ */
+ public List<ConfigurationStats> queryConfigurations(int intervalType, long beginTime,
+ long endTime) {
+ try {
+ @SuppressWarnings("unchecked")
+ ParceledListSlice<ConfigurationStats> slice = mService.queryConfigurationStats(
+ intervalType, beginTime, endTime, mContext.getOpPackageName());
+ if (slice != null) {
+ return slice.getList();
+ }
+ } catch (RemoteException e) {
+ // fallthrough and return the empty list.
+ }
+ return Collections.emptyList();
}
/**
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 119d705..083a48a 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -17,6 +17,7 @@
package android.app.usage;
import android.content.ComponentName;
+import android.content.res.Configuration;
/**
* UsageStatsManager local system service interface.
@@ -28,14 +29,19 @@
/**
* Reports an event to the UsageStatsManager.
*
- * @param component The component for which this event ocurred.
+ * @param component The component for which this event occurred.
* @param userId The user id to which the component belongs to.
- * @param timeStamp The time at which this event ocurred.
- * @param eventType The event that occured. Valid values can be found at
+ * @param eventType The event that occurred. Valid values can be found at
* {@link UsageEvents}
*/
- public abstract void reportEvent(ComponentName component, int userId,
- long timeStamp, int eventType);
+ public abstract void reportEvent(ComponentName component, int userId, int eventType);
+
+ /**
+ * Reports a configuration change to the UsageStatsManager.
+ *
+ * @param config The new device configuration.
+ */
+ public abstract void reportConfigurationChange(Configuration config, int userId);
/**
* Prepares the UsageStatsService for shutdown.
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index e63fd07..27bbb24 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -16,6 +16,12 @@
package android.content.res;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Parcel;
@@ -23,7 +29,7 @@
import android.text.TextUtils;
import android.view.View;
-import java.text.Format;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
@@ -1353,8 +1359,6 @@
* Returns a string representation of the configuration that can be parsed
* by build tools (like AAPT).
*
- *
- *
* @hide
*/
public static String resourceQualifierString(Configuration config) {
@@ -1568,4 +1572,229 @@
parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
return TextUtils.join("-", parts);
}
+
+ /**
+ * Generate a delta Configuration between <code>base</code> and <code>change</code>. The
+ * resulting delta can be used with {@link #updateFrom(Configuration)}.
+ * <p />
+ * Caveat: If the any of the Configuration's members becomes undefined, then
+ * {@link #updateFrom(Configuration)} will treat it as a no-op and not update that member.
+ *
+ * This is fine for device configurations as no member is ever undefined.
+ * {@hide}
+ */
+ public static Configuration generateDelta(Configuration base, Configuration change) {
+ final Configuration delta = new Configuration();
+ if (base.fontScale != change.fontScale) {
+ delta.fontScale = change.fontScale;
+ }
+
+ if (base.mcc != change.mcc) {
+ delta.mcc = change.mcc;
+ }
+
+ if (base.mnc != change.mnc) {
+ delta.mnc = change.mnc;
+ }
+
+ if ((base.locale == null && change.locale != null) ||
+ (base.locale != null && !base.locale.equals(change.locale))) {
+ delta.locale = change.locale;
+ }
+
+ if (base.touchscreen != change.touchscreen) {
+ delta.touchscreen = change.touchscreen;
+ }
+
+ if (base.keyboard != change.keyboard) {
+ delta.keyboard = change.keyboard;
+ }
+
+ if (base.keyboardHidden != change.keyboardHidden) {
+ delta.keyboardHidden = change.keyboardHidden;
+ }
+
+ if (base.navigation != change.navigation) {
+ delta.navigation = change.navigation;
+ }
+
+ if (base.navigationHidden != change.navigationHidden) {
+ delta.navigationHidden = change.navigationHidden;
+ }
+
+ if (base.orientation != change.orientation) {
+ delta.orientation = change.orientation;
+ }
+
+ if ((base.screenLayout & SCREENLAYOUT_SIZE_MASK) !=
+ (change.screenLayout & SCREENLAYOUT_SIZE_MASK)) {
+ delta.screenLayout |= change.screenLayout & SCREENLAYOUT_SIZE_MASK;
+ }
+
+ if ((base.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK) !=
+ (change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
+ delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
+ }
+
+ if ((base.screenLayout & SCREENLAYOUT_LONG_MASK) !=
+ (change.screenLayout & SCREENLAYOUT_LONG_MASK)) {
+ delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LONG_MASK;
+ }
+
+ if ((base.uiMode & UI_MODE_TYPE_MASK) != (change.uiMode & UI_MODE_TYPE_MASK)) {
+ delta.uiMode |= change.uiMode & UI_MODE_TYPE_MASK;
+ }
+
+ if ((base.uiMode & UI_MODE_NIGHT_MASK) != (change.uiMode & UI_MODE_NIGHT_MASK)) {
+ delta.uiMode |= change.uiMode & UI_MODE_NIGHT_MASK;
+ }
+
+ if (base.screenWidthDp != change.screenWidthDp) {
+ delta.screenWidthDp = change.screenWidthDp;
+ }
+
+ if (base.screenHeightDp != change.screenHeightDp) {
+ delta.screenHeightDp = change.screenHeightDp;
+ }
+
+ if (base.smallestScreenWidthDp != change.smallestScreenWidthDp) {
+ delta.smallestScreenWidthDp = change.smallestScreenWidthDp;
+ }
+
+ if (base.densityDpi != change.densityDpi) {
+ delta.densityDpi = change.densityDpi;
+ }
+ return delta;
+ }
+
+ private static final String XML_ATTR_FONT_SCALE = "fs";
+ private static final String XML_ATTR_MCC = "mcc";
+ private static final String XML_ATTR_MNC = "mnc";
+ private static final String XML_ATTR_LOCALE = "locale";
+ private static final String XML_ATTR_TOUCHSCREEN = "touch";
+ private static final String XML_ATTR_KEYBOARD = "key";
+ private static final String XML_ATTR_KEYBOARD_HIDDEN = "keyHid";
+ private static final String XML_ATTR_HARD_KEYBOARD_HIDDEN = "hardKeyHid";
+ private static final String XML_ATTR_NAVIGATION = "nav";
+ private static final String XML_ATTR_NAVIGATION_HIDDEN = "navHid";
+ private static final String XML_ATTR_ORIENTATION = "ori";
+ private static final String XML_ATTR_SCREEN_LAYOUT = "scrLay";
+ private static final String XML_ATTR_UI_MODE = "ui";
+ private static final String XML_ATTR_SCREEN_WIDTH = "width";
+ private static final String XML_ATTR_SCREEN_HEIGHT = "height";
+ private static final String XML_ATTR_SMALLEST_WIDTH = "sw";
+ private static final String XML_ATTR_DENSITY = "density";
+
+ /**
+ * Reads the attributes corresponding to Configuration member fields from the Xml parser.
+ * The parser is expected to be on a tag which has Configuration attributes.
+ *
+ * @param parser The Xml parser from which to read attributes.
+ * @param configOut The Configuration to populate from the Xml attributes.
+ * {@hide}
+ */
+ public static void readXmlAttrs(XmlPullParser parser, Configuration configOut)
+ throws XmlPullParserException, IOException {
+ configOut.fontScale = Float.intBitsToFloat(
+ XmlUtils.readIntAttribute(parser, XML_ATTR_FONT_SCALE, 0));
+ configOut.mcc = XmlUtils.readIntAttribute(parser, XML_ATTR_MCC, 0);
+ configOut.mnc = XmlUtils.readIntAttribute(parser, XML_ATTR_MNC, 0);
+
+ final String localeStr = XmlUtils.readStringAttribute(parser, XML_ATTR_LOCALE);
+ if (localeStr != null) {
+ configOut.locale = Locale.forLanguageTag(localeStr);
+ }
+
+ configOut.touchscreen = XmlUtils.readIntAttribute(parser, XML_ATTR_TOUCHSCREEN,
+ TOUCHSCREEN_UNDEFINED);
+ configOut.keyboard = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD,
+ KEYBOARD_UNDEFINED);
+ configOut.keyboardHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD_HIDDEN,
+ KEYBOARDHIDDEN_UNDEFINED);
+ configOut.hardKeyboardHidden =
+ XmlUtils.readIntAttribute(parser, XML_ATTR_HARD_KEYBOARD_HIDDEN,
+ HARDKEYBOARDHIDDEN_UNDEFINED);
+ configOut.navigation = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION,
+ NAVIGATION_UNDEFINED);
+ configOut.navigationHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION_HIDDEN,
+ NAVIGATIONHIDDEN_UNDEFINED);
+ configOut.orientation = XmlUtils.readIntAttribute(parser, XML_ATTR_ORIENTATION,
+ ORIENTATION_UNDEFINED);
+ configOut.screenLayout = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_LAYOUT,
+ SCREENLAYOUT_UNDEFINED);
+ configOut.uiMode = XmlUtils.readIntAttribute(parser, XML_ATTR_UI_MODE, 0);
+ configOut.screenWidthDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_WIDTH,
+ SCREEN_WIDTH_DP_UNDEFINED);
+ configOut.screenHeightDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_HEIGHT,
+ SCREEN_HEIGHT_DP_UNDEFINED);
+ configOut.smallestScreenWidthDp =
+ XmlUtils.readIntAttribute(parser, XML_ATTR_SMALLEST_WIDTH,
+ SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
+ configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
+ DENSITY_DPI_UNDEFINED);
+ }
+
+
+ /**
+ * Writes the Configuration's member fields as attributes into the XmlSerializer.
+ * The serializer is expected to have already started a tag so that attributes can be
+ * immediately written.
+ *
+ * @param xml The serializer to which to write the attributes.
+ * @param config The Configuration whose member fields to write.
+ * {@hide}
+ */
+ public static void writeXmlAttrs(XmlSerializer xml, Configuration config) throws IOException {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_FONT_SCALE,
+ Float.floatToIntBits(config.fontScale));
+ if (config.mcc != 0) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_MCC, config.mcc);
+ }
+ if (config.mnc != 0) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_MNC, config.mnc);
+ }
+ if (config.locale != null) {
+ XmlUtils.writeStringAttribute(xml, XML_ATTR_LOCALE, config.locale.toLanguageTag());
+ }
+ if (config.touchscreen != TOUCHSCREEN_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_TOUCHSCREEN, config.touchscreen);
+ }
+ if (config.keyboard != KEYBOARD_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD, config.keyboard);
+ }
+ if (config.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD_HIDDEN, config.keyboardHidden);
+ }
+ if (config.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_HARD_KEYBOARD_HIDDEN,
+ config.hardKeyboardHidden);
+ }
+ if (config.navigation != NAVIGATION_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION, config.navigation);
+ }
+ if (config.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION_HIDDEN, config.navigationHidden);
+ }
+ if (config.orientation != ORIENTATION_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_ORIENTATION, config.orientation);
+ }
+ if (config.screenLayout != SCREENLAYOUT_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_LAYOUT, config.screenLayout);
+ }
+ if (config.uiMode != 0) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_UI_MODE, config.uiMode);
+ }
+ if (config.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_WIDTH, config.screenWidthDp);
+ }
+ if (config.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_HEIGHT, config.screenHeightDp);
+ }
+ if (config.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_SMALLEST_WIDTH, config.smallestScreenWidthDp);
+ }
+ if (config.densityDpi != DENSITY_DPI_UNDEFINED) {
+ XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
+ }
+ }
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 83bdfaa..5a09b46 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -52,6 +52,9 @@
* @hide
*/
public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId) {
+ if (nc == null) {
+ throw new NullPointerException();
+ }
requestId = rId;
networkCapabilities = nc;
this.legacyType = legacyType;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 7dce348..2b53c48 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -727,10 +727,9 @@
int[] runs = dirs.mDirections;
int lineStart = getLineStart(line);
for (int i = 0; i < runs.length; i += 2) {
- int start = lineStart + (runs[i] & RUN_LENGTH_MASK);
- // No need to test the end as an offset after the last run should return the value
- // corresponding of the last run
- if (offset >= start) {
+ int start = lineStart + runs[i];
+ int limit = start + (runs[i+1] & RUN_LENGTH_MASK);
+ if (offset >= start && offset < limit) {
int level = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
return ((level & 1) != 0);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 21e7c6b..92ad4e8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -444,16 +444,19 @@
* <a name="Drawing"></a>
* <h3>Drawing</h3>
* <p>
- * Drawing is handled by walking the tree and rendering each view that
- * intersects the invalid region. Because the tree is traversed in-order,
- * this means that parents will draw before (i.e., behind) their children, with
- * siblings drawn in the order they appear in the tree.
- * If you set a background drawable for a View, then the View will draw it for you
- * before calling back to its <code>onDraw()</code> method.
+ * Drawing is handled by walking the tree and recording the drawing commands of
+ * any View that needs to update. After this, the drawing commands of the
+ * entire tree are issued to screen, clipped to the newly damaged area.
* </p>
*
* <p>
- * Note that the framework will not draw views that are not in the invalid region.
+ * The tree is largely recorded and drawn in order, with parents drawn before
+ * (i.e., behind) their children, with siblings drawn in the order they appear
+ * in the tree. If you set a background drawable for a View, then the View will
+ * draw it before calling back to its <code>onDraw()</code> method. The child
+ * drawing order can be overridden with
+ * {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean) custom child drawing order}
+ * in a ViewGroup, and with {@link #setZ(float)} custom Z values} set on Views.
* </p>
*
* <p>
@@ -10825,6 +10828,12 @@
/**
* Sets whether the View's Outline should be used to clip the contents of the View.
* <p>
+ * Only a single non-rectangular clip can be applied on a View at any time.
+ * Circular clips from a {@link ViewAnimationUtils#createCircularReveal(View, int, int, float, float)
+ * circular reveal} animation take priority over Outline clipping, and
+ * child Outline clipping takes priority over Outline clipping done by a
+ * parent.
+ * <p>
* Note that this flag will only be respected if the View's Outline returns true from
* {@link Outline#canClip()}.
*
@@ -15091,10 +15100,12 @@
bounds.set(0, 0, mRight - mLeft, mBottom - mTop);
}
+ canvas.save();
canvas.translate(mScrollX, mScrollY);
+ canvas.clipRect(bounds, Region.Op.REPLACE);
drawable.setBounds(bounds);
drawable.draw(canvas);
- canvas.translate(-mScrollX, -mScrollY);
+ canvas.restore();
}
/**
diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java
index 7ced088..001cd01 100644
--- a/core/java/android/view/ViewAnimationUtils.java
+++ b/core/java/android/view/ViewAnimationUtils.java
@@ -27,9 +27,13 @@
private ViewAnimationUtils() {}
/**
* Returns an Animator which can animate a clipping circle.
- *
+ * <p>
* Any shadow cast by the View will respect the circular clip from this animator.
- *
+ * <p>
+ * Only a single non-rectangular clip can be applied on a View at any time.
+ * Views clipped by a circular reveal animation take priority over
+ * {@link View#setClipToOutline(boolean) View Outline clipping}.
+ * <p>
* Note that the animation returned here is a one-shot animation. It cannot
* be re-used, and once started it cannot be paused or resumed.
*
@@ -39,7 +43,7 @@
* @param startRadius The starting radius of the animating circle.
* @param endRadius The ending radius of the animating circle.
*/
- public static final Animator createCircularReveal(View view,
+ public static Animator createCircularReveal(View view,
int centerX, int centerY, float startRadius, float endRadius) {
return new RevealAnimator(view, centerX, centerY, startRadius, endRadius);
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index adad082..974fe4e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5034,6 +5034,9 @@
/**
* Tells the ViewGroup whether to draw its children in the order defined by the method
* {@link #getChildDrawingOrder(int, int)}.
+ * <p>
+ * Note that {@link View#getZ() Z} reordering, done by {@link #dispatchDraw(Canvas)},
+ * will override custom child ordering done via this method.
*
* @param enabled true if the order of the children when drawing is determined by
* {@link #getChildDrawingOrder(int, int)}, false otherwise
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 26c1f96..2729bd0 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -71,9 +71,9 @@
* @attr ref android.R.styleable#DatePicker_minDate
* @attr ref android.R.styleable#DatePicker_spinnersShown
* @attr ref android.R.styleable#DatePicker_calendarViewShown
- * @attr ref android.R.styleable#DatePicker_dayOfWeekBackgroundColor
+ * @attr ref android.R.styleable#DatePicker_dayOfWeekBackground
* @attr ref android.R.styleable#DatePicker_dayOfWeekTextAppearance
- * @attr ref android.R.styleable#DatePicker_headerBackgroundColor
+ * @attr ref android.R.styleable#DatePicker_headerBackground
* @attr ref android.R.styleable#DatePicker_headerMonthTextAppearance
* @attr ref android.R.styleable#DatePicker_headerDayOfMonthTextAppearance
* @attr ref android.R.styleable#DatePicker_headerYearTextAppearance
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 7df1fa3..eed49bf 100644
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -21,7 +21,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.format.DateFormat;
@@ -149,16 +149,12 @@
mDayOfWeekView.setTextAppearance(context, dayOfWeekTextAppearanceResId);
}
- final int dayOfWeekBackgroundColor = a.getColor(
- R.styleable.DatePicker_dayOfWeekBackgroundColor, Color.TRANSPARENT);
- mDayOfWeekView.setBackgroundColor(dayOfWeekBackgroundColor);
+ mDayOfWeekView.setBackground(a.getDrawable(R.styleable.DatePicker_dayOfWeekBackground));
+
+ dateLayout.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
final int headerSelectedTextColor = a.getColor(
R.styleable.DatePicker_headerSelectedTextColor, defaultHighlightColor);
- final int headerBackgroundColor = a.getColor(R.styleable.DatePicker_headerBackgroundColor,
- Color.TRANSPARENT);
- dateLayout.setBackgroundColor(headerBackgroundColor);
-
final int monthTextAppearanceResId = a.getResourceId(
R.styleable.DatePicker_headerMonthTextAppearance, -1);
if (monthTextAppearanceResId != -1) {
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index adca4cc..d2f68d0 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -458,6 +458,7 @@
a.recycle();
setOnTouchListener(this);
+ setClickable(true);
// Initial values
final Calendar calendar = Calendar.getInstance(Locale.getDefault());
@@ -612,9 +613,9 @@
mMinutesTexts[i] = String.format("%02d", MINUTES_NUMBERS[i]);
}
- String[] amPmTexts = new DateFormatSymbols().getAmPmStrings();
- mAmPmText[AM] = amPmTexts[0];
- mAmPmText[PM] = amPmTexts[1];
+ String[] amPmStrings = TimePickerClockDelegate.getAmPmStrings(mContext);
+ mAmPmText[AM] = amPmStrings[0];
+ mAmPmText[PM] = amPmStrings[1];
}
private void initData() {
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 6169d2e..73e05e8 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -21,7 +21,6 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -40,7 +39,6 @@
import com.android.internal.R;
-import java.text.DateFormatSymbols;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
@@ -71,6 +69,7 @@
private static final int HOURS_IN_HALF_DAY = 12;
+ private View mHeaderView;
private TextView mHourView;
private TextView mMinuteView;
private TextView mAmPmTextView;
@@ -156,11 +155,8 @@
mAmPmTextView.setTextAppearance(context, headerAmPmTextAppearance);
}
- final int headerBackgroundColor = a.getColor(
- R.styleable.TimePicker_headerBackgroundColor, Color.TRANSPARENT);
- if (headerBackgroundColor != Color.TRANSPARENT) {
- mainView.findViewById(R.id.time_header).setBackgroundColor(headerBackgroundColor);
- }
+ mHeaderView = mainView.findViewById(R.id.time_header);
+ mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
a.recycle();
@@ -194,14 +190,11 @@
}
private void setupListeners() {
- KeyboardListener keyboardListener = new KeyboardListener();
- mDelegator.setOnKeyListener(keyboardListener);
+ mHeaderView.setOnKeyListener(mKeyListener);
+ mHeaderView.setOnFocusChangeListener(mFocusListener);
+ mHeaderView.setFocusable(true);
- mHourView.setOnKeyListener(keyboardListener);
- mMinuteView.setOnKeyListener(keyboardListener);
- mAmPmTextView.setOnKeyListener(keyboardListener);
mRadialTimePickerView.setOnValueSelectedListener(this);
- mRadialTimePickerView.setOnKeyListener(keyboardListener);
mHourView.setOnClickListener(new View.OnClickListener() {
@Override
@@ -641,7 +634,7 @@
if (!isTypedTimeFullyLegal()) {
mTypedTimes.clear();
}
- finishKbMode(true);
+ finishKbMode();
}
}
@@ -776,27 +769,7 @@
* @return true if the key was successfully processed, false otherwise.
*/
private boolean processKeyUp(int keyCode) {
- if (keyCode == KeyEvent.KEYCODE_ESCAPE || keyCode == KeyEvent.KEYCODE_TAB) {
- if(mInKbMode) {
- if (isTypedTimeFullyLegal()) {
- finishKbMode(true);
- }
- return true;
- }
- } else if (keyCode == KeyEvent.KEYCODE_ENTER) {
- if (mInKbMode) {
- if (!isTypedTimeFullyLegal()) {
- return true;
- }
- finishKbMode(false);
- }
- if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator,
- mRadialTimePickerView.getCurrentHour(),
- mRadialTimePickerView.getCurrentMinute());
- }
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_DEL) {
+ if (keyCode == KeyEvent.KEYCODE_DEL) {
if (mInKbMode) {
if (!mTypedTimes.isEmpty()) {
int deleted = deleteLastTypedKey();
@@ -925,9 +898,8 @@
/**
* Get out of keyboard mode. If there is nothing in typedTimes, revert to TimePicker's time.
- * @param updateDisplays If true, update the displays with the relevant time.
*/
- private void finishKbMode(boolean updateDisplays) {
+ private void finishKbMode() {
mInKbMode = false;
if (!mTypedTimes.isEmpty()) {
int values[] = getEnteredTime(null);
@@ -938,10 +910,8 @@
}
mTypedTimes.clear();
}
- if (updateDisplays) {
- updateDisplay(false);
- mRadialTimePickerView.setInputEnabled(true);
- }
+ updateDisplay(false);
+ mRadialTimePickerView.setInputEnabled(true);
}
/**
@@ -1261,7 +1231,7 @@
}
}
- private class KeyboardListener implements View.OnKeyListener {
+ private final View.OnKeyListener mKeyListener = new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) {
@@ -1269,5 +1239,20 @@
}
return false;
}
- }
+ };
+
+ private final View.OnFocusChangeListener mFocusListener = new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (!hasFocus && mInKbMode && isTypedTimeFullyLegal()) {
+ finishKbMode();
+
+ if (mOnTimeChangedListener != null) {
+ mOnTimeChangedListener.onTimeChanged(mDelegator,
+ mRadialTimePickerView.getCurrentHour(),
+ mRadialTimePickerView.getCurrentMinute());
+ }
+ }
+ }
+ };
}
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/core/res/res/drawable/time_picker_header_material.xml
new file mode 100644
index 0000000..cdb92b6
--- /dev/null
+++ b/core/res/res/drawable/time_picker_header_material.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlHighlight">
+ <item>
+ <color android:color="?attr/colorAccent" />
+ </item>
+</ripple>
diff --git a/core/res/res/layout-land/time_picker_holo.xml b/core/res/res/layout-land/time_picker_holo.xml
index f316f1b..ce90a5b 100644
--- a/core/res/res/layout-land/time_picker_holo.xml
+++ b/core/res/res/layout-land/time_picker_holo.xml
@@ -21,7 +21,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
- android:focusable="true"
android:layout_marginLeft="@dimen/timepicker_minimum_margin_sides"
android:layout_marginRight="@dimen/timepicker_minimum_margin_sides"
android:layout_marginTop="@dimen/timepicker_minimum_margin_top_bottom"
@@ -42,7 +41,5 @@
android:id="@+id/radial_picker"
android:layout_width="@dimen/timepicker_radial_picker_dimen"
android:layout_height="match_parent"
- android:layout_gravity="center"
- android:focusable="true"
- android:focusableInTouchMode="true" />
+ android:layout_gravity="center" />
</LinearLayout>
diff --git a/core/res/res/layout/time_picker_holo.xml b/core/res/res/layout/time_picker_holo.xml
index 483eb6d..08d2211 100644
--- a/core/res/res/layout/time_picker_holo.xml
+++ b/core/res/res/layout/time_picker_holo.xml
@@ -18,20 +18,17 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:focusable="true" >
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<include
- layout="@layout/time_header_label"
- android:layout_width="match_parent"
- android:layout_height="@dimen/timepicker_header_height"
- android:layout_gravity="center" />
+ layout="@layout/time_header_label"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/timepicker_header_height"
+ android:layout_gravity="center" />
<android.widget.RadialTimePickerView
- android:id="@+id/radial_picker"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/timepicker_radial_picker_dimen"
- android:layout_gravity="center"
- android:focusable="true"
- android:focusableInTouchMode="true" />
+ android:id="@+id/radial_picker"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/timepicker_radial_picker_dimen"
+ android:layout_gravity="center" />
</LinearLayout>
diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml
index 8069d13..edf6d9f 100644
--- a/core/res/res/values-mcc310-mnc410/config.xml
+++ b/core/res/res/values-mcc310-mnc410/config.xml
@@ -46,4 +46,9 @@
<item>GPS_LOCK=1</item>
<item>LPP_PROFILE=3</item>
</string-array>
+ <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
+ <string-array name="config_twoDigitNumberPattern">
+ <item>"0"</item>
+ <item>"00"</item>
+ </string-array>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4622995..603fcde 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4346,7 +4346,7 @@
<!-- @hide The layout of the legacy DatePicker. -->
<attr name="legacyLayout" />
<!-- The background color for the date selector 's day of week. -->
- <attr name="dayOfWeekBackgroundColor" format="color" />
+ <attr name="dayOfWeekBackground" format="color|reference" />
<!-- The text color for the date selector's day of week. -->
<attr name="dayOfWeekTextAppearance" format="reference" />
<!-- The month's text appearance in the date selector. -->
@@ -4355,8 +4355,8 @@
<attr name="headerDayOfMonthTextAppearance" format="reference" />
<!-- The year's text appearance in the date selector. -->
<attr name="headerYearTextAppearance" format="reference" />
- <!-- The background color for the date selector. -->
- <attr name="headerBackgroundColor" />
+ <!-- The background for the date selector. -->
+ <attr name="headerBackground" />
<!-- @hide The selected text color for the date selector. Used as a
backup if the text appearance does not explicitly have a color
set for the selected state. -->
@@ -4653,29 +4653,29 @@
<attr name="legacyLayout" format="reference" />
<!-- @hide The layout of the time picker. -->
<attr name="internalLayout" />
- <!-- The text appearance for the AM/PM header of the TimePicker. -->
+ <!-- The text appearance for the AM/PM header. -->
<attr name="headerAmPmTextAppearance" format="reference" />
- <!-- The text appearance for the time header of the TimePicker. -->
+ <!-- The text appearance for the time header. -->
<attr name="headerTimeTextAppearance" format="reference" />
<!-- @hide The text color for selected time header of the TimePicker.
This will override the value from the text appearance if it does
not explicitly have a color set for the selected state. -->
<attr name="headerSelectedTextColor" format="color" />
- <!-- The background color for the header of the TimePicker. -->
- <attr name="headerBackgroundColor" format="color" />
- <!-- The color for the hours/minutes numbers of the TimePicker. -->
+ <!-- The background for the header containing the currently selected time. -->
+ <attr name="headerBackground" />
+ <!-- The color for the hours/minutes numbers. -->
<attr name="numbersTextColor" format="color" />
- <!-- The background color for the hours/minutes numbers of the TimePicker. -->
+ <!-- The background color for the hours/minutes numbers. -->
<attr name="numbersBackgroundColor" format="color" />
- <!-- The color for the AM/PM selectors of the TimePicker. -->
+ <!-- The color for the AM/PM selectors. -->
<attr name="amPmTextColor" format="color" />
- <!-- The background color state list for the AM/PM selectors of the TimePicker. -->
+ <!-- The background color state list for the AM/PM selectors. -->
<attr name="amPmBackgroundColor" format="color" />
<!-- @hide The background color for the AM/PM selectors of the
TimePicker when selected. Used if the background color does not
explicitly have a color set for the selected state. -->
<attr name="amPmSelectedBackgroundColor" format="color" />
- <!-- The color for the hours/minutes selector of the TimePicker. -->
+ <!-- The color for the hours/minutes selector. -->
<attr name="numbersSelectorColor" format="color" />
<!-- Defines the look of the widget. Prior to the L release, the only choice was
spinner. As of L, with the Material theme selected, the default layout is clock,
@@ -7435,6 +7435,9 @@
<!-- @removed -->
<attr name="__removed1" format="reference" />
+ <!-- TODO: Spacer to be removed from here and public.xml -->
+ <attr name="__removed2" format="reference" />
+
<!-- Attributes that can be used with <code>rating-system-definition</code> tags inside of the
XML resource that describes TV content rating of a {@link android.media.tv.TvInputService},
which is referenced from its
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 75c0e2c..4bf4531 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2235,7 +2235,7 @@
<public type="attr" name="launchTaskBehindTargetAnimation" />
<public type="attr" name="launchTaskBehindSourceAnimation" />
<public type="attr" name="restrictionType" />
- <public type="attr" name="dayOfWeekBackgroundColor" />
+ <public type="attr" name="dayOfWeekBackground" />
<public type="attr" name="dayOfWeekTextAppearance" />
<public type="attr" name="headerMonthTextAppearance" />
<public type="attr" name="headerDayOfMonthTextAppearance" />
@@ -2248,7 +2248,7 @@
<public type="attr" name="timePickerDialogTheme" />
<public type="attr" name="headerTimeTextAppearance" />
<public type="attr" name="headerAmPmTextAppearance" />
- <public type="attr" name="headerBackgroundColor" />
+ <public type="attr" name="__removed2" />
<public type="attr" name="numbersTextColor" />
<public type="attr" name="numbersBackgroundColor" />
<public type="attr" name="numbersSelectorColor" />
diff --git a/core/res/res/values/styles_holo.xml b/core/res/res/values/styles_holo.xml
index 2a54ccf..c7d2db1 100644
--- a/core/res/res/values/styles_holo.xml
+++ b/core/res/res/values/styles_holo.xml
@@ -468,7 +468,7 @@
<item name="internalLayout">@layout/time_picker_holo</item>
<item name="headerTimeTextAppearance">@style/TextAppearance.Holo.TimePicker.TimeLabel</item>
<item name="headerAmPmTextAppearance">@style/TextAppearance.Holo.TimePicker.AmPmLabel</item>
- <item name="headerBackgroundColor">@color/timepicker_default_background_holo_dark</item>
+ <item name="headerBackground">@color/timepicker_default_background_holo_dark</item>
<item name="headerSelectedTextColor">@color/holo_blue_light</item>
<item name="numbersTextColor">@color/timepicker_default_text_color_holo_dark</item>
<item name="numbersBackgroundColor">@color/timepicker_default_background_holo_dark</item>
@@ -484,9 +484,9 @@
<item name="internalLayout">@layout/date_picker_holo</item>
<item name="calendarViewShown">true</item>
<!-- New-style date picker attributes. -->
- <item name="dayOfWeekBackgroundColor">@color/datepicker_default_header_dayofweek_background_color_holo_dark</item>
+ <item name="dayOfWeekBackground">@color/datepicker_default_header_dayofweek_background_color_holo_dark</item>
<item name="dayOfWeekTextAppearance">@style/TextAppearance.Holo.DatePicker.DayOfWeekLabel</item>
- <item name="headerBackgroundColor">@color/datepicker_default_header_selector_background_holo_dark</item>
+ <item name="headerBackground">@color/datepicker_default_header_selector_background_holo_dark</item>
<item name="headerMonthTextAppearance">@style/TextAppearance.Holo.DatePicker.Selector.MonthLabel</item>
<item name="headerDayOfMonthTextAppearance">@style/TextAppearance.Holo.DatePicker.Selector.DayOfMonthLabel</item>
<item name="headerYearTextAppearance">@style/TextAppearance.Holo.DatePicker.Selector.YearLabel</item>
@@ -892,7 +892,7 @@
<item name="internalLayout">@layout/time_picker_holo</item>
<item name="headerTimeTextAppearance">@style/TextAppearance.Holo.Light.TimePicker.TimeLabel</item>
<item name="headerAmPmTextAppearance">@style/TextAppearance.Holo.Light.TimePicker.AmPmLabel</item>
- <item name="headerBackgroundColor">@color/timepicker_default_background_holo_light</item>
+ <item name="headerBackground">@color/timepicker_default_background_holo_light</item>
<item name="headerSelectedTextColor">@color/holo_blue_light</item>
<item name="numbersTextColor">@color/timepicker_default_text_color_holo_light</item>
<item name="numbersBackgroundColor">@color/timepicker_default_background_holo_light</item>
@@ -908,12 +908,12 @@
<item name="internalLayout">@layout/date_picker_holo</item>
<item name="calendarViewShown">true</item>
<!-- New-style date picker attributes. -->
- <item name="dayOfWeekBackgroundColor">@color/datepicker_default_header_dayofweek_background_color_holo_light</item>
+ <item name="dayOfWeekBackground">@color/datepicker_default_header_dayofweek_background_color_holo_light</item>
<item name="dayOfWeekTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.DayOfWeekLabel</item>
<item name="headerMonthTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.Selector.MonthLabel</item>
<item name="headerDayOfMonthTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.Selector.DayOfMonthLabel</item>
<item name="headerYearTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.Selector.YearLabel</item>
- <item name="headerBackgroundColor">@color/datepicker_default_header_selector_background_holo_light</item>
+ <item name="headerBackground">@color/datepicker_default_header_selector_background_holo_light</item>
<item name="headerSelectedTextColor">@color/holo_blue_light</item>
<item name="yearListItemTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.List.YearLabel</item>
<item name="yearListSelectorColor">@color/datepicker_default_circle_background_color_holo_light</item>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 77484e6..a7335af 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -619,7 +619,7 @@
<item name="headerTimeTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
<item name="headerAmPmTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
<item name="headerSelectedTextColor">?attr/textColorPrimaryInverse</item>
- <item name="headerBackgroundColor">?attr/colorAccent</item>
+ <item name="headerBackground">@drawable/time_picker_header_material</item>
<item name="numbersTextColor">?attr/textColorSecondary</item>
<item name="numbersBackgroundColor">#10ffffff</item>
<item name="amPmTextColor">?attr/textColorSecondary</item>
@@ -634,13 +634,13 @@
<!-- Attributes for new-style DatePicker. -->
<item name="internalLayout">@layout/date_picker_holo</item>
<item name="calendarViewShown">true</item>
- <item name="dayOfWeekBackgroundColor">#10000000</item>
+ <item name="dayOfWeekBackground">#10000000</item>
<item name="dayOfWeekTextAppearance">@style/TextAppearance.Material.DatePicker.DayOfWeekLabel</item>
<item name="headerMonthTextAppearance">@style/TextAppearance.Material.DatePicker.MonthLabel</item>
<item name="headerDayOfMonthTextAppearance">@style/TextAppearance.Material.DatePicker.DayOfMonthLabel</item>
<item name="headerYearTextAppearance">@style/TextAppearance.Material.DatePicker.YearLabel</item>
<item name="headerSelectedTextColor">?attr/textColorPrimaryInverse</item>
- <item name="headerBackgroundColor">?attr/colorAccent</item>
+ <item name="headerBackground">?attr/colorAccent</item>
<item name="yearListItemTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel</item>
<item name="yearListSelectorColor">?attr/colorControlActivated</item>
<item name="calendarTextColor">?attr/textColorSecondary</item>
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index cd919a6..43922b8 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -419,18 +419,22 @@
private void endSoftwareAnimations() {
if (mAnimRadius != null) {
mAnimRadius.end();
+ mAnimRadius = null;
}
if (mAnimOpacity != null) {
mAnimOpacity.end();
+ mAnimOpacity = null;
}
if (mAnimX != null) {
mAnimX.end();
+ mAnimX = null;
}
if (mAnimY != null) {
mAnimY.end();
+ mAnimY = null;
}
}
@@ -506,18 +510,22 @@
private void cancelSoftwareAnimations() {
if (mAnimRadius != null) {
mAnimRadius.cancel();
+ mAnimRadius = null;
}
if (mAnimOpacity != null) {
mAnimOpacity.cancel();
+ mAnimOpacity = null;
}
if (mAnimX != null) {
mAnimX.cancel();
+ mAnimX = null;
}
if (mAnimY != null) {
mAnimY.cancel();
+ mAnimY = null;
}
}
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 4e68a60..80ecea3 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -326,6 +326,7 @@
private void endSoftwareAnimations() {
if (mAnimOuterOpacity != null) {
mAnimOuterOpacity.end();
+ mAnimOuterOpacity = null;
}
}
@@ -413,6 +414,7 @@
private void cancelSoftwareAnimations() {
if (mAnimOuterOpacity != null) {
mAnimOuterOpacity.cancel();
+ mAnimOuterOpacity = null;
}
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 6a92a6e..8fb1f64 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -413,7 +413,7 @@
handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
- // TODO: support both reveal clip and outline clip simultaneously
+ // TODO: support nesting round rect clips
if (mProperties.getRevealClip().willClip()) {
Rect bounds;
mProperties.getRevealClip().getBounds(&bounds);
@@ -421,7 +421,6 @@
} else if (mProperties.getOutline().willClip()) {
renderer.setClippingOutline(handler.allocator(), &(mProperties.getOutline()));
}
-
}
/**
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index ecc47d2..cf8229fd 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -216,14 +216,22 @@
// Clipping round rect
///////////////////////////////////////////////////////////////////////////////
-void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius) {
+void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds,
+ float radius, bool highPriority) {
if (bounds.isEmpty()) {
clipRect->setEmpty();
return;
}
+ if (roundRectClipState && roundRectClipState->highPriority) {
+ // ignore, don't replace, already have a high priority clip
+ return;
+ }
+
RoundRectClipState* state = new (allocator) RoundRectClipState;
+ state->highPriority = highPriority;
+
// store the inverse drawing matrix
Matrix4 roundRectDrawingMatrix;
roundRectDrawingMatrix.load(getOrthoMatrix());
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index ad4ee9d..549de9b 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -55,6 +55,7 @@
|| rect.intersects(dangerRects[3]);
}
+ bool highPriority;
Matrix4 matrix;
Rect dangerRects[4];
Rect innerRect;
@@ -166,8 +167,11 @@
/**
* Sets (and replaces) the current clipping outline
+ *
+ * If the current round rect clip is high priority, the incoming clip is ignored.
*/
- void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius);
+ void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds,
+ float radius, bool highPriority);
/**
* Indicates whether this snapshot should be ignored. A snapshot
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index 3e1aed3..12b8c8d 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -198,13 +198,13 @@
clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkRegion::kIntersect_Op);
}
if (outlineIsRounded) {
- setClippingRoundRect(allocator, bounds, radius);
+ setClippingRoundRect(allocator, bounds, radius, false);
}
}
void StatefulBaseRenderer::setClippingRoundRect(LinearAllocator& allocator,
- const Rect& rect, float radius) {
- mSnapshot->setClippingRoundRect(allocator, rect, radius);
+ const Rect& rect, float radius, bool highPriority) {
+ mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
}
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index c6974b4..745e48a 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -97,7 +97,7 @@
*/
void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
void setClippingRoundRect(LinearAllocator& allocator,
- const Rect& rect, float radius);
+ const Rect& rect, float radius, bool highPriority = true);
inline const mat4* currentTransform() const {
return mSnapshot->transform;
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index bdd1195..fcf222b 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -16,6 +16,7 @@
package android.location;
+import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -771,6 +772,7 @@
* @see #makeComplete
* @hide
*/
+ @SystemApi
public boolean isComplete() {
if (mProvider == null) return false;
if (!mHasAccuracy) return false;
@@ -788,6 +790,7 @@
* @see #isComplete
* @hide
*/
+ @SystemApi
public void makeComplete() {
if (mProvider == null) mProvider = "?";
if (!mHasAccuracy) {
@@ -957,6 +960,7 @@
* @param isFromMockProvider true if this Location came from a mock provider, false otherwise
* @hide
*/
+ @SystemApi
public void setIsFromMockProvider(boolean isFromMockProvider) {
mIsFromMockProvider = isFromMockProvider;
}
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index c9162fe..271f2bb 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -16,6 +16,7 @@
package android.location;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -84,6 +85,7 @@
*
* @hide
*/
+@SystemApi
public final class LocationRequest implements Parcelable {
/**
* Used with {@link #setQuality} to request the most accurate locations available.
@@ -166,6 +168,7 @@
}
/** @hide */
+ @SystemApi
public static LocationRequest createFromDeprecatedProvider(String provider, long minTime,
float minDistance, boolean singleShot) {
if (minTime < 0) minTime = 0;
@@ -191,6 +194,7 @@
}
/** @hide */
+ @SystemApi
public static LocationRequest createFromDeprecatedCriteria(Criteria criteria, long minTime,
float minDistance, boolean singleShot) {
if (minTime < 0) minTime = 0;
@@ -475,6 +479,7 @@
/** @hide */
+ @SystemApi
public LocationRequest setProvider(String provider) {
checkProvider(provider);
mProvider = provider;
@@ -482,11 +487,13 @@
}
/** @hide */
+ @SystemApi
public String getProvider() {
return mProvider;
}
/** @hide */
+ @SystemApi
public LocationRequest setSmallestDisplacement(float meters) {
checkDisplacement(meters);
mSmallestDisplacement = meters;
@@ -494,6 +501,7 @@
}
/** @hide */
+ @SystemApi
public float getSmallestDisplacement() {
return mSmallestDisplacement;
}
@@ -508,11 +516,13 @@
* @param workSource WorkSource defining power blame for this location request.
* @hide
*/
+ @SystemApi
public void setWorkSource(WorkSource workSource) {
mWorkSource = workSource;
}
/** @hide */
+ @SystemApi
public WorkSource getWorkSource() {
return mWorkSource;
}
@@ -531,11 +541,13 @@
* @see android.app.AppOpsManager
* @hide
*/
+ @SystemApi
public void setHideFromAppOps(boolean hideFromAppOps) {
mHideFromAppOps = hideFromAppOps;
}
/** @hide */
+ @SystemApi
public boolean getHideFromAppOps() {
return mHideFromAppOps;
}
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index 8d2c172..9db4994 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -47,10 +47,7 @@
mListeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
// find the looper for our new event handler
- Looper looper = Looper.myLooper();
- if (looper == null) {
- looper = Looper.getMainLooper();
- }
+ Looper looper = Looper.getMainLooper();
if (looper != null) {
mHandler = new Handler(looper) {
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index f378cef..fbfac89 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -789,8 +789,12 @@
void startListeningToSessions() {
final ComponentName listenerComponent = new ComponentName(mContext,
mOnClientUpdateListener.getClass());
+ Handler handler = null;
+ if (Looper.myLooper() == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
mSessionManager.addOnActiveSessionsChangedListener(mSessionListener, listenerComponent,
- UserHandle.myUserId(), null);
+ UserHandle.myUserId(), handler);
mSessionListener.onActiveSessionsChanged(mSessionManager
.getActiveSessions(listenerComponent));
if (DEBUG) {
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index 6e1402c..c4a55d1 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -23,6 +23,7 @@
LOCAL_PACKAGE_NAME := PrintSpooler
+LOCAL_JNI_SHARED_LIBRARIES := libprintspooler_jni
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v7-recyclerview
include $(BUILD_PACKAGE)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b116d76..023f627 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3215,7 +3215,6 @@
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
- System.currentTimeMillis(),
UsageEvents.Event.MOVE_TO_FOREGROUND);
}
synchronized (stats) {
@@ -3224,7 +3223,6 @@
} else {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
- System.currentTimeMillis(),
UsageEvents.Event.MOVE_TO_BACKGROUND);
}
synchronized (stats) {
@@ -15936,6 +15934,7 @@
newConfig.seq = mConfigurationSeq;
mConfiguration = newConfig;
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);
+ mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);
//mUsageStatsService.noteStartConfig(newConfig);
final Configuration configCopy = new Configuration(mConfiguration);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cad2772..3e6191c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2996,10 +2996,12 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
- synchronized (this) {
- DevicePolicyData policy = getUserData(userHandle);
- long ident = Binder.clearCallingIdentity();
- try {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ boolean wipeData = false;
+ int identifier = 0;
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(userHandle);
policy.mFailedPasswordAttempts++;
saveSettingsLocked(userHandle);
if (mHasFeature) {
@@ -3011,15 +3013,20 @@
// Wipe the user/profile associated with the policy that was violated. This
// is not necessarily calling user: if the policy that fired was from a
// managed profile rather than the main user profile, we wipe former only.
- wipeDeviceOrUserLocked(0, strictestAdmin.getUserHandle().getIdentifier());
+ wipeData = true;
+ identifier = strictestAdmin.getUserHandle().getIdentifier();
}
sendAdminCommandToSelfAndProfilesLocked(
DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
+ if (wipeData) {
+ // Call without holding lock.
+ wipeDeviceOrUserLocked(0, identifier);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index dc036e2..6c80a65 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -15,17 +15,23 @@
*/
package com.android.server.usage;
+import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
+import android.content.res.Configuration;
import android.util.ArrayMap;
import android.util.ArraySet;
+import java.util.ArrayList;
+
class IntervalStats {
public long beginTime;
public long endTime;
public long lastTimeSaved;
public final ArrayMap<String, UsageStats> stats = new ArrayMap<>();
+ public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
+ public Configuration activeConfiguration;
public TimeSparseArray<UsageEvents.Event> events;
// A string cache. This is important as when we're parsing XML files, we don't want to
@@ -34,18 +40,49 @@
// strings that had identical copies in the cache.
private final ArraySet<String> mStringCache = new ArraySet<>();
+ /**
+ * Gets the UsageStats object for the given package, or creates one and adds it internally.
+ */
UsageStats getOrCreateUsageStats(String packageName) {
UsageStats usageStats = stats.get(packageName);
if (usageStats == null) {
usageStats = new UsageStats();
- usageStats.mPackageName = packageName;
+ usageStats.mPackageName = getCachedStringRef(packageName);
usageStats.mBeginTimeStamp = beginTime;
usageStats.mEndTimeStamp = endTime;
- stats.put(packageName, usageStats);
+ stats.put(usageStats.mPackageName, usageStats);
}
return usageStats;
}
+ /**
+ * Gets the ConfigurationStats object for the given configuration, or creates one and adds it
+ * internally.
+ */
+ ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
+ ConfigurationStats configStats = configurations.get(config);
+ if (configStats == null) {
+ configStats = new ConfigurationStats();
+ configStats.mBeginTimeStamp = beginTime;
+ configStats.mEndTimeStamp = endTime;
+ configStats.mConfiguration = config;
+ configurations.put(config, configStats);
+ }
+ return configStats;
+ }
+
+ /**
+ * Builds a UsageEvents.Event, but does not add it internally.
+ */
+ UsageEvents.Event buildEvent(String packageName, String className) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ event.mPackage = getCachedStringRef(packageName);
+ if (className != null) {
+ event.mClass = getCachedStringRef(className);
+ }
+ return event;
+ }
+
void update(String packageName, long timeStamp, int eventType) {
UsageStats usageStats = getOrCreateUsageStats(packageName);
@@ -61,6 +98,28 @@
usageStats.mLastEvent = eventType;
usageStats.mLastTimeUsed = timeStamp;
usageStats.mEndTimeStamp = timeStamp;
+
+ if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+ usageStats.mLaunchCount += 1;
+ }
+
+ endTime = timeStamp;
+ }
+
+ void updateConfigurationStats(Configuration config, long timeStamp) {
+ if (activeConfiguration != null) {
+ ConfigurationStats activeStats = configurations.get(activeConfiguration);
+ activeStats.mTotalTimeActive += timeStamp - activeStats.mLastTimeActive;
+ activeStats.mLastTimeActive = timeStamp - 1;
+ }
+
+ if (config != null) {
+ ConfigurationStats configStats = getOrCreateConfigurationStats(config);
+ configStats.mLastTimeActive = timeStamp;
+ configStats.mActivationCount += 1;
+ activeConfiguration = configStats.mConfiguration;
+ }
+
endTime = timeStamp;
}
@@ -72,13 +131,4 @@
}
return mStringCache.valueAt(index);
}
-
- UsageEvents.Event buildEvent(String packageName, String className) {
- UsageEvents.Event event = new UsageEvents.Event();
- event.mPackage = getCachedStringRef(packageName);
- if (className != null) {
- event.mClass = getCachedStringRef(className);
- }
- return event;
- }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index e6ce0fe..37340a4 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,7 +17,6 @@
package com.android.server.usage;
import android.app.usage.TimeSparseArray;
-import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.util.AtomicFile;
import android.util.Slog;
@@ -133,9 +132,30 @@
}
/**
- * Find all {@link UsageStats} for the given range and interval type.
+ * Figures out what to extract from the given IntervalStats object.
*/
- public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
+ interface StatCombiner<T> {
+
+ /**
+ * Implementations should extract interesting from <code>stats</code> and add it
+ * to the <code>accumulatedResult</code> list.
+ *
+ * If the <code>stats</code> object is mutable, <code>mutable</code> will be true,
+ * which means you should make a copy of the data before adding it to the
+ * <code>accumulatedResult</code> list.
+ *
+ * @param stats The {@link IntervalStats} object selected.
+ * @param mutable Whether or not the data inside the stats object is mutable.
+ * @param accumulatedResult The list to which to add extracted data.
+ */
+ void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
+ }
+
+ /**
+ * Find all {@link IntervalStats} for the given range and interval type.
+ */
+ public <T> List<T> queryUsageStats(int intervalType, long beginTime, long endTime,
+ StatCombiner<T> combiner) {
synchronized (mLock) {
if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
throw new IllegalArgumentException("Bad interval type " + intervalType);
@@ -157,7 +177,7 @@
try {
IntervalStats stats = new IntervalStats();
- ArrayList<UsageStats> results = new ArrayList<>();
+ ArrayList<T> results = new ArrayList<>();
for (int i = startIndex; i <= endIndex; i++) {
final AtomicFile f = mSortedStatFiles[intervalType].valueAt(i);
@@ -167,7 +187,7 @@
UsageStatsXml.read(f, stats);
if (beginTime < stats.endTime) {
- results.addAll(stats.stats.values());
+ combiner.combine(stats, false, results);
}
}
return results;
@@ -209,6 +229,10 @@
public void prune() {
synchronized (mLock) {
long timeNow = System.currentTimeMillis();
+ mCal.setTimeInMillis(timeNow);
+ mCal.add(Calendar.YEAR, -3);
+ pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_YEARLY],
+ mCal.getTimeInMillis());
mCal.setTimeInMillis(timeNow);
mCal.add(Calendar.MONTH, -6);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0e8b427..e77bf86 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.app.AppOpsManager;
+import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
@@ -30,11 +31,13 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
@@ -218,8 +221,7 @@
* Called by the Binder stub.
*/
List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime) {
- final long timeNow = System.currentTimeMillis();
- if (beginTime > timeNow) {
+ if (!validRange(beginTime, endTime)) {
return null;
}
@@ -232,15 +234,23 @@
/**
* Called by the Binder stub.
*/
+ List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime,
+ long endTime) {
+ if (!validRange(beginTime, endTime)) {
+ return null;
+ }
+
+ synchronized (mLock) {
+ UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId);
+ return service.queryConfigurationStats(bucketType, beginTime, endTime);
+ }
+ }
+
+ /**
+ * Called by the Binder stub.
+ */
UsageEvents queryEvents(int userId, long beginTime, long endTime) {
- final long timeNow = System.currentTimeMillis();
-
- // Adjust the endTime so that we don't query for the latest events.
- // This is to prevent apps from making decision based on what app launched them,
- // etc.
- endTime = Math.min(endTime, timeNow - END_TIME_DELAY);
-
- if (beginTime > endTime) {
+ if (!validRange(beginTime, endTime)) {
return null;
}
@@ -250,6 +260,11 @@
}
}
+ private static boolean validRange(long beginTime, long endTime) {
+ final long timeNow = System.currentTimeMillis();
+ return beginTime <= timeNow && beginTime < endTime;
+ }
+
private void flushToDiskLocked() {
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
@@ -323,6 +338,28 @@
}
@Override
+ public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType,
+ long beginTime, long endTime, String callingPackage) throws RemoteException {
+ if (!hasPermission(callingPackage)) {
+ return null;
+ }
+
+ final int userId = UserHandle.getCallingUserId();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final List<ConfigurationStats> results =
+ UsageStatsService.this.queryConfigurationStats(userId, bucketType,
+ beginTime, endTime);
+ if (results != null) {
+ return new ParceledListSlice<>(results);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return null;
+ }
+
+ @Override
public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
if (!hasPermission(callingPackage)) {
return null;
@@ -346,8 +383,7 @@
private class LocalService extends UsageStatsManagerInternal {
@Override
- public void reportEvent(ComponentName component, int userId,
- long timeStamp, int eventType) {
+ public void reportEvent(ComponentName component, int userId, int eventType) {
if (component == null) {
Slog.w(TAG, "Event reported without a component name");
return;
@@ -356,12 +392,27 @@
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = component.getPackageName();
event.mClass = component.getClassName();
- event.mTimeStamp = timeStamp;
+ event.mTimeStamp = System.currentTimeMillis();
event.mEventType = eventType;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@Override
+ public void reportConfigurationChange(Configuration config, int userId) {
+ if (config == null) {
+ Slog.w(TAG, "Configuration event reported with a null config");
+ return;
+ }
+
+ UsageEvents.Event event = new UsageEvents.Event();
+ event.mPackage = "android";
+ event.mTimeStamp = System.currentTimeMillis();
+ event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
+ event.mConfiguration = new Configuration(config);
+ mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+ }
+
+ @Override
public void prepareShutdown() {
// This method *WILL* do IO work, but we must block until it is finished or else
// we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 374429a..6529950 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -20,68 +20,69 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.content.ComponentName;
+import android.content.res.Configuration;
import java.io.IOException;
import java.net.ProtocolException;
+import java.util.Locale;
/**
* UsageStats reader/writer for version 1 of the XML format.
*/
final class UsageStatsXmlV1 {
+ private static final String PACKAGE_TAG = "package";
+ private static final String CONFIGURATION_TAG = "config";
+ private static final String EVENT_LOG_TAG = "event-log";
+
private static final String BEGIN_TIME_ATTR = "beginTime";
private static final String END_TIME_ATTR = "endTime";
- private static final String PACKAGE_TAG = "package";
private static final String NAME_ATTR = "name";
private static final String PACKAGE_ATTR = "package";
private static final String CLASS_ATTR = "class";
private static final String TOTAL_TIME_ACTIVE_ATTR = "totalTimeActive";
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+ private static final String COUNT_ATTR = "count";
+ private static final String ACTIVE_ATTR = "active";
private static final String LAST_EVENT_ATTR = "lastEvent";
- private static final String EVENT_LOG_TAG = "event-log";
private static final String TYPE_ATTR = "type";
private static final String TIME_ATTR = "time";
- private static UsageStats readNextUsageStats(XmlPullParser parser)
+ private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- XmlUtils.nextElement(parser);
- }
-
- if (parser.getEventType() != XmlPullParser.START_TAG ||
- !parser.getName().equals(PACKAGE_TAG)) {
- return null;
- }
-
final String name = parser.getAttributeValue(null, NAME_ATTR);
if (name == null) {
throw new ProtocolException("no " + NAME_ATTR + " attribute present");
}
- UsageStats stats = new UsageStats();
- stats.mPackageName = name;
+ UsageStats stats = statsOut.getOrCreateUsageStats(name);
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
stats.mLastTimeUsed = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
- XmlUtils.skipCurrentTag(parser);
- return stats;
}
- private static UsageEvents.Event readNextEvent(XmlPullParser parser, IntervalStats statsOut)
+ private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- XmlUtils.nextElement(parser);
- }
+ final Configuration config = new Configuration();
+ Configuration.readXmlAttrs(parser, config);
- if (parser.getEventType() != XmlPullParser.START_TAG ||
- !parser.getName().equals(EVENT_LOG_TAG)) {
- return null;
+ ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
+ configStats.mLastTimeActive = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
+ configStats.mTotalTimeActive = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
+ configStats.mActivationCount = XmlUtils.readIntAttribute(parser, COUNT_ATTR);
+ if (XmlUtils.readBooleanAttribute(parser, ACTIVE_ATTR)) {
+ statsOut.activeConfiguration = configStats.mConfiguration;
}
+ }
+ private static void loadEvent(XmlPullParser parser, IntervalStats statsOut)
+ throws XmlPullParserException, IOException {
String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
String className;
if (packageName == null) {
@@ -105,31 +106,60 @@
UsageEvents.Event event = statsOut.buildEvent(packageName, className);
event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
event.mTimeStamp = XmlUtils.readLongAttribute(parser, TIME_ATTR);
- XmlUtils.skipCurrentTag(parser);
- return event;
- }
- private static void writeUsageStats(FastXmlSerializer serializer, UsageStats stats)
- throws IOException {
- serializer.startTag(null, PACKAGE_TAG);
- serializer.attribute(null, NAME_ATTR, stats.mPackageName);
- serializer.attribute(null, TOTAL_TIME_ACTIVE_ATTR,
- Long.toString(stats.mTotalTimeInForeground));
- serializer.attribute(null, LAST_TIME_ACTIVE_ATTR, Long.toString(stats.mLastTimeUsed));
- serializer.attribute(null, LAST_EVENT_ATTR, Integer.toString(stats.mLastEvent));
- serializer.endTag(null, PACKAGE_TAG);
- }
-
- private static void writeEvent(FastXmlSerializer serializer, UsageEvents.Event event)
- throws IOException {
- serializer.startTag(null, EVENT_LOG_TAG);
- serializer.attribute(null, PACKAGE_ATTR, event.mPackage);
- if (event.mClass != null) {
- serializer.attribute(null, CLASS_ATTR, event.mClass);
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+ event.mConfiguration = new Configuration();
+ Configuration.readXmlAttrs(parser, event.mConfiguration);
}
- serializer.attribute(null, TYPE_ATTR, Integer.toString(event.getEventType()));
- serializer.attribute(null, TIME_ATTR, Long.toString(event.getTimeStamp()));
- serializer.endTag(null, EVENT_LOG_TAG);
+
+ if (statsOut.events == null) {
+ statsOut.events = new TimeSparseArray<>();
+ }
+ statsOut.events.put(event.mTimeStamp, event);
+ }
+
+ private static void writeUsageStats(XmlSerializer xml, final UsageStats stats)
+ throws IOException {
+ xml.startTag(null, PACKAGE_TAG);
+ XmlUtils.writeStringAttribute(xml, NAME_ATTR, stats.mPackageName);
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeInForeground);
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeUsed);
+ XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, stats.mLastEvent);
+ xml.endTag(null, PACKAGE_TAG);
+ }
+
+ private static void writeConfigStats(XmlSerializer xml, final ConfigurationStats stats,
+ boolean isActive) throws IOException {
+ xml.startTag(null, CONFIGURATION_TAG);
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeActive);
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeActive);
+ XmlUtils.writeIntAttribute(xml, COUNT_ATTR, stats.mActivationCount);
+ if (isActive) {
+ XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true);
+ }
+
+ // Now write the attributes representing the configuration object.
+ Configuration.writeXmlAttrs(xml, stats.mConfiguration);
+
+ xml.endTag(null, CONFIGURATION_TAG);
+ }
+
+ private static void writeEvent(XmlSerializer xml, final UsageEvents.Event event)
+ throws IOException {
+ xml.startTag(null, EVENT_LOG_TAG);
+ XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage);
+ if (event.mClass != null) {
+ XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass);
+ }
+ XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
+ XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp);
+
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE
+ && event.mConfiguration != null) {
+ Configuration.writeXmlAttrs(xml, event.mConfiguration);
+ }
+
+ xml.endTag(null, EVENT_LOG_TAG);
}
/**
@@ -142,6 +172,8 @@
public static void read(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
statsOut.stats.clear();
+ statsOut.configurations.clear();
+ statsOut.activeConfiguration = null;
if (statsOut.events != null) {
statsOut.events.clear();
@@ -149,21 +181,29 @@
statsOut.beginTime = XmlUtils.readLongAttribute(parser, BEGIN_TIME_ATTR);
statsOut.endTime = XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
- XmlUtils.nextElement(parser);
- UsageStats pkgStats;
- while ((pkgStats = readNextUsageStats(parser)) != null) {
- pkgStats.mBeginTimeStamp = statsOut.beginTime;
- pkgStats.mEndTimeStamp = statsOut.endTime;
- statsOut.stats.put(pkgStats.mPackageName, pkgStats);
- }
-
- UsageEvents.Event event;
- while ((event = readNextEvent(parser, statsOut)) != null) {
- if (statsOut.events == null) {
- statsOut.events = new TimeSparseArray<>();
+ int eventCode;
+ int outerDepth = parser.getDepth();
+ while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (eventCode != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (eventCode != XmlPullParser.START_TAG) {
+ continue;
}
- statsOut.events.put(event.getTimeStamp(), event);
+
+ final String tag = parser.getName();
+ switch (tag) {
+ case PACKAGE_TAG:
+ loadUsageStats(parser, statsOut);
+ break;
+
+ case CONFIGURATION_TAG:
+ loadConfigStats(parser, statsOut);
+ break;
+
+ case EVENT_LOG_TAG:
+ loadEvent(parser, statsOut);
+ break;
+ }
}
}
@@ -184,11 +224,15 @@
writeUsageStats(serializer, stats.stats.valueAt(i));
}
- if (stats.events != null) {
- final int eventCount = stats.events.size();
- for (int i = 0; i < eventCount; i++) {
- writeEvent(serializer, stats.events.valueAt(i));
- }
+ final int configCount = stats.configurations.size();
+ for (int i = 0; i < configCount; i++) {
+ boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
+ writeConfigStats(serializer, stats.configurations.valueAt(i), active);
+ }
+
+ final int eventCount = stats.events != null ? stats.events.size() : 0;
+ for (int i = 0; i < eventCount; i++) {
+ writeEvent(serializer, stats.events.valueAt(i));
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 6951590..7142a99 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -16,13 +16,17 @@
package com.android.server.usage;
+import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
+import android.content.res.Configuration;
import android.util.ArraySet;
import android.util.Slog;
+import com.android.server.usage.UsageStatsDatabase.StatCombiner;
+
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
@@ -108,35 +112,91 @@
notifyStatsChanged();
}
}
+
+ stat.updateConfigurationStats(null, stat.lastTimeSaved);
}
}
void reportEvent(UsageEvents.Event event) {
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
- + "[" + event.getTimeStamp() + "]: "
- + eventToString(event.getEventType()));
+ + "[" + event.mTimeStamp + "]: "
+ + eventToString(event.mEventType));
}
- if (event.getTimeStamp() >= mDailyExpiryDate.getTimeInMillis()) {
+ if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover
rolloverStats();
}
- if (mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events == null) {
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events = new TimeSparseArray<>();
+ final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
+
+ final Configuration newFullConfig = event.mConfiguration;
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
+ currentDailyStats.activeConfiguration != null) {
+ // Make the event configuration a delta.
+ event.mConfiguration = Configuration.generateDelta(
+ currentDailyStats.activeConfiguration, newFullConfig);
}
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events.put(event.getTimeStamp(), event);
+
+ // Add the event to the daily list.
+ if (currentDailyStats.events == null) {
+ currentDailyStats.events = new TimeSparseArray<>();
+ }
+ currentDailyStats.events.put(event.mTimeStamp, event);
for (IntervalStats stats : mCurrentStats) {
- stats.update(event.mPackage, event.getTimeStamp(),
- event.getEventType());
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+ stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
+ } else {
+ stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ }
}
notifyStatsChanged();
}
- List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
+ private static final StatCombiner<UsageStats> sUsageStatsCombiner =
+ new StatCombiner<UsageStats>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<UsageStats> accResult) {
+ if (!mutable) {
+ accResult.addAll(stats.stats.values());
+ return;
+ }
+
+ final int statCount = stats.stats.size();
+ for (int i = 0; i < statCount; i++) {
+ accResult.add(new UsageStats(stats.stats.valueAt(i)));
+ }
+ }
+ };
+
+ private static final StatCombiner<ConfigurationStats> sConfigStatsCombiner =
+ new StatCombiner<ConfigurationStats>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<ConfigurationStats> accResult) {
+ if (!mutable) {
+ accResult.addAll(stats.configurations.values());
+ return;
+ }
+
+ final int configCount = stats.configurations.size();
+ for (int i = 0; i < configCount; i++) {
+ accResult.add(new ConfigurationStats(stats.configurations.valueAt(i)));
+ }
+ }
+ };
+
+ /**
+ * Generic query method that selects the appropriate IntervalStats for the specified time range
+ * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
+ * provided to select the stats to use from the IntervalStats object.
+ */
+ private <T> List<T> queryStats(int bucketType, long beginTime, long endTime,
+ StatCombiner<T> combiner) {
if (bucketType == UsageStatsManager.INTERVAL_BEST) {
bucketType = mDatabase.findBestFitBucket(beginTime, endTime);
}
@@ -161,11 +221,8 @@
Slog.d(TAG, mLogPrefix + "Returning in-memory stats for bucket " + bucketType);
}
// Fast path for retrieving in-memory state.
- ArrayList<UsageStats> results = new ArrayList<>();
- final int packageCount = mCurrentStats[bucketType].stats.size();
- for (int i = 0; i < packageCount; i++) {
- results.add(new UsageStats(mCurrentStats[bucketType].stats.valueAt(i)));
- }
+ ArrayList<T> results = new ArrayList<>();
+ combiner.combine(mCurrentStats[bucketType], true, results);
return results;
}
@@ -178,13 +235,21 @@
+ beginTime + " AND endTime < " + endTime);
}
- final List<UsageStats> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime);
+ final List<T> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime, combiner);
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Results: " + (results == null ? 0 : results.size()));
}
return results;
}
+ List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
+ return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
+ }
+
+ List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
+ return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
+ }
+
UsageEvents queryEvents(long beginTime, long endTime) {
if (endTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime) {
if (beginTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].endTime) {
@@ -245,6 +310,8 @@
// Finish any ongoing events with an END_OF_DAY event. Make a note of which components
// need a new CONTINUE_PREVIOUS_DAY entry.
+ final Configuration previousConfig =
+ mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
ArraySet<String> continuePreviousDay = new ArraySet<>();
for (IntervalStats stat : mCurrentStats) {
final int pkgCount = stat.stats.size();
@@ -253,11 +320,13 @@
if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
continuePreviousDay.add(pkgStats.mPackageName);
- stat.update(pkgStats.mPackageName,
- mDailyExpiryDate.getTimeInMillis() - 1, UsageEvents.Event.END_OF_DAY);
+ stat.update(pkgStats.mPackageName, mDailyExpiryDate.getTimeInMillis() - 1,
+ UsageEvents.Event.END_OF_DAY);
mStatsChanged = true;
}
}
+
+ stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
}
persistActiveStats();
@@ -267,9 +336,10 @@
final int continueCount = continuePreviousDay.size();
for (int i = 0; i < continueCount; i++) {
String name = continuePreviousDay.valueAt(i);
+ final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
for (IntervalStats stat : mCurrentStats) {
- stat.update(name, mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime,
- UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ stat.update(name, beginTime, UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ stat.updateConfigurationStats(previousConfig, beginTime);
mStatsChanged = true;
}
}
@@ -353,6 +423,8 @@
return "END_OF_DAY";
case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
return "CONTINUE_PREVIOUS_DAY";
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ return "CONFIGURATION_CHANGE";
default:
return "UNKNOWN";
}
diff --git a/tests/UsageStatsTest/Android.mk b/tests/UsageStatsTest/Android.mk
index 69fefeb..5f7467a 100644
--- a/tests/UsageStatsTest/Android.mk
+++ b/tests/UsageStatsTest/Android.mk
@@ -6,6 +6,8 @@
# Only compile source java files in this apk.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+
LOCAL_PACKAGE_NAME := UsageStatsTest
include $(BUILD_PACKAGE)
diff --git a/tests/UsageStatsTest/res/layout/config_row_item.xml b/tests/UsageStatsTest/res/layout/config_row_item.xml
new file mode 100644
index 0000000..547de04
--- /dev/null
+++ b/tests/UsageStatsTest/res/layout/config_row_item.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"/>
diff --git a/tests/UsageStatsTest/res/layout/row_item.xml b/tests/UsageStatsTest/res/layout/row_item.xml
index da50163..4f2bfe4 100644
--- a/tests/UsageStatsTest/res/layout/row_item.xml
+++ b/tests/UsageStatsTest/res/layout/row_item.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
@@ -8,13 +9,10 @@
<TextView android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
+ android:layout_weight="1"
android:textStyle="bold"/>
<TextView android:id="@android:id/text2"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignBaseline="@android:id/text1"/>
-</RelativeLayout>
+ android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
index 5f62ad8..31e7c38 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
+import android.support.v4.util.CircularArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -35,11 +36,14 @@
private UsageStatsManager mUsageStatsManager;
private Adapter mAdapter;
private Handler mHandler = new Handler();
+ private long mLastTime;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+ mLastTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
+
mAdapter = new Adapter();
setListAdapter(mAdapter);
}
@@ -59,24 +63,31 @@
@Override
public void run() {
long now = System.currentTimeMillis();
- long beginTime = now - USAGE_STATS_PERIOD;
- UsageEvents events = mUsageStatsManager.queryEvents(beginTime, now);
- mAdapter.update(events);
+ UsageEvents events = mUsageStatsManager.queryEvents(mLastTime, now);
+ long lastEventTime = mAdapter.update(events);
+ if (lastEventTime >= 0) {
+ mLastTime = lastEventTime + 1;
+ }
mHandler.postDelayed(this, 1000 * 5);
}
private class Adapter extends BaseAdapter {
+ private static final int MAX_EVENTS = 50;
+ private final CircularArray<UsageEvents.Event> mEvents = new CircularArray<>(MAX_EVENTS);
- private final ArrayList<UsageEvents.Event> mEvents = new ArrayList<>();
-
- public void update(UsageEvents results) {
- mEvents.clear();
+ public long update(UsageEvents results) {
+ long lastTimeStamp = -1;
while (results.hasNextEvent()) {
UsageEvents.Event event = new UsageEvents.Event();
results.getNextEvent(event);
- mEvents.add(event);
+ lastTimeStamp = event.getTimeStamp();
+ if (mEvents.size() == MAX_EVENTS) {
+ mEvents.popLast();
+ }
+ mEvents.addFirst(event);
}
notifyDataSetChanged();
+ return lastTimeStamp;
}
@Override
@@ -85,7 +96,7 @@
}
@Override
- public Object getItem(int position) {
+ public UsageEvents.Event getItem(int position) {
return mEvents.get(position);
}
@@ -95,41 +106,72 @@
}
@Override
+ public int getItemViewType(int position) {
+ final int eventType = getItem(position).getEventType();
+ if (eventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+ return 1;
+ }
+ return 0;
+ }
+
+ @Override
public View getView(int position, View convertView, ViewGroup parent) {
+ final UsageEvents.Event event = getItem(position);
+
final ViewHolder holder;
if (convertView == null) {
- convertView = LayoutInflater.from(UsageLogActivity.this)
- .inflate(R.layout.row_item, parent, false);
holder = new ViewHolder();
- holder.packageName = (TextView) convertView.findViewById(android.R.id.text1);
- holder.state = (TextView) convertView.findViewById(android.R.id.text2);
+
+ if (event.getEventType() == UsageEvents.Event.CONFIGURATION_CHANGE) {
+ convertView = LayoutInflater.from(UsageLogActivity.this)
+ .inflate(R.layout.config_row_item, parent, false);
+ holder.config = (TextView) convertView.findViewById(android.R.id.text1);
+ } else {
+ convertView = LayoutInflater.from(UsageLogActivity.this)
+ .inflate(R.layout.row_item, parent, false);
+ holder.packageName = (TextView) convertView.findViewById(android.R.id.text1);
+ holder.state = (TextView) convertView.findViewById(android.R.id.text2);
+ }
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
- holder.packageName.setText(mEvents.get(position).getPackageName());
- String state;
- switch (mEvents.get(position).getEventType()) {
+ if (holder.packageName != null) {
+ holder.packageName.setText(event.getPackageName());
+ }
+
+ if (holder.state != null) {
+ holder.state.setText(eventToString(event.getEventType()));
+ }
+
+ if (holder.config != null &&
+ event.getEventType() == UsageEvents.Event.CONFIGURATION_CHANGE) {
+ holder.config.setText(event.getConfiguration().toString());
+ }
+ return convertView;
+ }
+
+ private String eventToString(int eventType) {
+ switch (eventType) {
case UsageEvents.Event.MOVE_TO_FOREGROUND:
- state = "Foreground";
- break;
+ return "Foreground";
case UsageEvents.Event.MOVE_TO_BACKGROUND:
- state = "Background";
- break;
+ return "Background";
+
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ return "Config change";
default:
- state = "Unknown: " + mEvents.get(position).getEventType();
- break;
+ return "Unknown: " + eventType;
}
- holder.state.setText(state);
- return convertView;
}
}
static class ViewHolder {
public TextView packageName;
public TextView state;
+ public TextView config;
}
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 74f2f65..1393bce 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1000,6 +1000,7 @@
}
/** @hide */
+ @SystemApi
public boolean startScan(WorkSource workSource) {
try {
mService.startScan(null, workSource);
@@ -1073,6 +1074,7 @@
* @return false if not supported.
* @hide
*/
+ @SystemApi
public boolean isBatchedScanSupported() {
try {
return mService.isBatchedScanSupported();
@@ -1099,6 +1101,7 @@
* {@link BATCHED_SCAN_RESULTS_AVAILABLE_ACTION} is received.
* @hide
*/
+ @SystemApi
public List<BatchedScanResult> getBatchedScanResults() {
try {
return mService.getBatchedScanResults(mContext.getOpPackageName());