diff options
36 files changed, 1079 insertions, 304 deletions
diff --git a/api/current.txt b/api/current.txt index a69a8e5f98a0..f7c0b975e038 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5391,8 +5391,8 @@ package android.app.admin { field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED"; - field public static final java.lang.String ACTION_LOCK_TASK_ENTERING = "android.app.action.ACTION_LOCK_TASK_ENTERING"; - field public static final java.lang.String ACTION_LOCK_TASK_EXITING = "android.app.action.ACTION_LOCK_TASK_EXITING"; + field public static final java.lang.String ACTION_LOCK_TASK_ENTERING = "android.app.action.LOCK_TASK_ENTERING"; + field public static final java.lang.String ACTION_LOCK_TASK_EXITING = "android.app.action.LOCK_TASK_EXITING"; field public static final java.lang.String ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED"; field public static final java.lang.String ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING"; field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED"; @@ -5706,6 +5706,19 @@ package android.app.job { 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 @@ package android.app.usage { 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 @@ package android.app.usage { 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 @@ -17306,6 +17322,7 @@ package android.net { method public android.net.Uri getPacFileUrl(); method public int getPort(); method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; } public abstract class PskKeyManager { @@ -28888,6 +28905,7 @@ package android.telephony { field public static final java.lang.String MMS_CONFIG_ALLOW_ATTACH_AUDIO = "allowAttachAudio"; field public static final java.lang.String MMS_CONFIG_APPEND_TRANSACTION_ID = "enabledTransID"; field public static final java.lang.String MMS_CONFIG_EMAIL_GATEWAY_NUMBER = "emailGatewayNumber"; + field public static final java.lang.String MMS_CONFIG_GROUP_MMS_ENABLED = "enableGroupMms"; field public static final java.lang.String MMS_CONFIG_HTTP_PARAMS = "httpParams"; field public static final java.lang.String MMS_CONFIG_HTTP_SOCKET_TIMEOUT = "httpSocketTimeout"; field public static final java.lang.String MMS_CONFIG_MAX_IMAGE_HEIGHT = "maxImageHeight"; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index f8dfdd9d9925..5921477eb893 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -21,7 +21,6 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -825,6 +824,13 @@ public class Notification implements Parcelable public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions"; /** + * {@link #extras} key: the user that built the notification. + * + * @hide + */ + public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId"; + + /** * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be * displayed in the heads up space. * @@ -1807,12 +1813,10 @@ public class Notification implements Parcelable = "android.rebuild.hudViewActionCount"; /** - * The ApplicationInfo of the package that created the notification, used to create - * a context to rebuild the notification via a Builder. - * @hide + * The package name of the context used to create the notification via a Builder. */ - private static final String EXTRA_REBUILD_CONTEXT_APPLICATION_INFO = - "android.rebuild.applicationInfo"; + private static final String EXTRA_REBUILD_CONTEXT_PACKAGE = + "android.rebuild.contextPackage"; // Whether to enable stripping (at post time) & rebuilding (at listener receive time) of // memory intensive resources. @@ -1863,6 +1867,11 @@ public class Notification implements Parcelable private int mColor = COLOR_DEFAULT; /** + * The user that built the notification originally. + */ + private int mOriginatingUserId; + + /** * Contains extras related to rebuilding during the build phase. */ private Bundle mRebuildBundle = new Bundle(); @@ -2582,7 +2591,7 @@ public class Notification implements Parcelable // Note: This assumes that the current user can read the profile badge of the // originating user. UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - return userManager.getBadgeForUser(new UserHandle(mContext.getUserId()), 0); + return userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0); } private Bitmap getProfileBadge() { @@ -2661,7 +2670,8 @@ public class Notification implements Parcelable * @param hasProgress whether the progress bar should be shown and set */ private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) { - RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId); + RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(), + mOriginatingUserId, resId); resetStandardTemplate(contentView); @@ -3053,8 +3063,8 @@ public class Notification implements Parcelable */ public void populateExtras(Bundle extras) { // Store original information used in the construction of this object - extras.putParcelable(EXTRA_REBUILD_CONTEXT_APPLICATION_INFO, - mContext.getApplicationInfo()); + extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId); + extras.putString(EXTRA_REBUILD_CONTEXT_PACKAGE, mContext.getPackageName()); extras.putCharSequence(EXTRA_TITLE, mContentTitle); extras.putCharSequence(EXTRA_TEXT, mContentText); extras.putCharSequence(EXTRA_SUB_TEXT, mSubText); @@ -3142,14 +3152,13 @@ public class Notification implements Parcelable extras.remove(EXTRA_NEEDS_REBUILD); // Re-create notification context so we can access app resources. - ApplicationInfo applicationInfo = extras.getParcelable( - EXTRA_REBUILD_CONTEXT_APPLICATION_INFO); + String packageName = extras.getString(EXTRA_REBUILD_CONTEXT_PACKAGE); Context builderContext; try { - builderContext = context.createApplicationContext(applicationInfo, + builderContext = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED); } catch (NameNotFoundException e) { - Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found"); + Log.e(TAG, "Package name " + packageName + " not found"); builderContext = context; // try with our context } @@ -3284,6 +3293,7 @@ public class Notification implements Parcelable // Extras. Bundle extras = n.extras; + mOriginatingUserId = extras.getInt(EXTRA_ORIGINATING_USERID); mContentTitle = extras.getCharSequence(EXTRA_TITLE); mContentText = extras.getCharSequence(EXTRA_TEXT); mSubText = extras.getCharSequence(EXTRA_SUB_TEXT); @@ -3316,6 +3326,7 @@ public class Notification implements Parcelable * object. */ public Notification build() { + mOriginatingUserId = mContext.getUserId(); mHasThreeLines = hasThreeLines(); Notification n = buildUnstyled(); @@ -4815,8 +4826,8 @@ public class Notification implements Parcelable super(parcel); } - public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) { - super(appInfo, layoutId); + public BuilderRemoteViews(String packageName, int userId, int layoutId) { + super(packageName, userId, layoutId); } @Override diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 15def097d5e7..e2f175c10bf9 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -177,7 +177,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LOCK_TASK_ENTERING - = "android.app.action.ACTION_LOCK_TASK_ENTERING"; + = "android.app.action.LOCK_TASK_ENTERING"; /** * Action sent to a device administrator to notify that the device is exiting @@ -190,7 +190,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LOCK_TASK_EXITING - = "android.app.action.ACTION_LOCK_TASK_EXITING"; + = "android.app.action.LOCK_TASK_EXITING"; /** * A boolean describing whether the device is currently entering or exiting diff --git a/core/java/android/app/usage/ConfigurationStats.java b/core/java/android/app/usage/ConfigurationStats.java new file mode 100644 index 000000000000..080216ce3b25 --- /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 3b098881e73f..4ed148911a43 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -27,5 +27,7 @@ import android.content.pm.ParceledListSlice; 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 fb80de2f4700..1a947ecf6c90 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 final class UsageEvents implements Parcelable { 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 final class UsageEvents implements Parcelable { 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 final class UsageEvents implements Parcelable { 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 @@ public final class UsageEvents implements Parcelable { 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 @@ public final class UsageEvents implements Parcelable { } } - @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 @@ public final class UsageEvents implements Parcelable { 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 @@ public final class UsageEvents implements Parcelable { 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 5830fcf49e89..bc6099a7d5e3 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -125,9 +125,9 @@ public final class UsageStatsManager { * @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 @@ public final class UsageStatsManager { } 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 119d7054b1b5..083a48a292d3 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 @@ public abstract class UsageStatsManagerInternal { /** * 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 e63fd0703339..27bbb242f575 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.os.Parcelable; 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration * 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration 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/ProxyInfo.java b/core/java/android/net/ProxyInfo.java index 7ea6bae83d2c..1534e2c7f4b1 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/core/java/android/net/ProxyInfo.java @@ -334,10 +334,6 @@ public class ProxyInfo implements Parcelable { dest.writeStringArray(mParsedExclusionList); } - /** - * Implement the Parcelable interface. - * @hide - */ public static final Creator<ProxyInfo> CREATOR = new Creator<ProxyInfo>() { public ProxyInfo createFromParcel(Parcel in) { diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 7dce34875d49..2b53c4862d44 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -727,10 +727,9 @@ public abstract class Layout { 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/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 1e411b45b6cb..9fec9a1f5a31 100755 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -17,7 +17,6 @@ package android.text.format; import android.content.Context; -import android.os.UserHandle; import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -129,14 +128,8 @@ public class DateFormat { * @return true if 24 hour time format is selected, false otherwise. */ public static boolean is24HourFormat(Context context) { - // This method is called by View classes that can be used by RemoteViews - // and rendered in another user. The context therefore may reference - // a user that would require interact accross users to access. So - // use the user id we are running as. - // This is the case when we have widgets from a user profile added - // to the homescreen. - String value = Settings.System.getStringForUser(context.getContentResolver(), - Settings.System.TIME_12_24, UserHandle.myUserId()); + String value = Settings.System.getString(context.getContentResolver(), + Settings.System.TIME_12_24); if (value == null) { Locale locale = context.getResources().getConfiguration().locale; @@ -234,14 +227,8 @@ public class DateFormat { * @return the {@link java.text.DateFormat} object that properly formats the date. */ public static java.text.DateFormat getDateFormat(Context context) { - // This method is called by View classes that can be used by RemoteViews - // and rendered in another user. The context therefore may reference - // a user that would require interact accross users to access. So - // use the user id we are running as. - // This is the case when we have widgets from a user profile added - // to the homescreen. - String value = Settings.System.getStringForUser(context.getContentResolver(), - Settings.System.DATE_FORMAT, UserHandle.myUserId()); + String value = Settings.System.getString(context.getContentResolver(), + Settings.System.DATE_FORMAT); return getDateFormatForSetting(context, value); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index dd1cbc936b17..4299e2e73e1d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6111,6 +6111,33 @@ public final class ViewRootImpl implements ViewParent, } } } break; + + + case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: { + if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView != null) { + // We care only for changes rooted in the focused host. + final long eventSourceId = event.getSourceNodeId(); + final int hostViewId = AccessibilityNodeInfo.getAccessibilityViewId( + eventSourceId); + if (hostViewId != mAccessibilityFocusedHost.getAccessibilityViewId()) { + break; + } + + // We only care about changes that may change the virtual focused view bounds. + final int changes = event.getContentChangeTypes(); + if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0 + || changes == AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) { + AccessibilityNodeProvider provider = mAccessibilityFocusedHost + .getAccessibilityNodeProvider(); + if (provider != null) { + final int virtualChildId = AccessibilityNodeInfo.getVirtualDescendantId( + mAccessibilityFocusedVirtualView.getSourceNodeId()); + mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo( + virtualChildId); + } + } + } + } break; } mAccessibilityManager.sendAccessibilityEvent(event); return true; diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java index b3ff88b3dabf..45d1403bc598 100644 --- a/core/java/android/widget/DateTimeView.java +++ b/core/java/android/widget/DateTimeView.java @@ -29,7 +29,6 @@ import android.util.Log; import android.provider.Settings; import android.widget.TextView; import android.widget.RemoteViews.RemoteView; -import android.os.UserHandle; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -191,15 +190,8 @@ public class DateTimeView extends TextView { } private DateFormat getDateFormat() { - // OK, this is gross but needed. This class is supported by the - // remote views mechanism and as a part of that the remote views - // can be inflated by a context for another user without the app - // having interact users permission - just for loading resources. - // For example, when adding widgets from a user profile to the - // home screen. Therefore, we access settings as the user the app - // is running as not the one the context is for. - String format = Settings.System.getStringForUser(getContext().getContentResolver(), - Settings.System.DATE_FORMAT, UserHandle.myUserId()); + String format = Settings.System.getString(getContext().getContentResolver(), + Settings.System.DATE_FORMAT); if (format == null || "".equals(format)) { return DateFormat.getDateInstance(DateFormat.SHORT); } else { @@ -220,20 +212,10 @@ public class DateTimeView extends TextView { filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - - // OK, this is gross but needed. This class is supported by the - // remote views mechanism and as a part of that the remote views - // can be inflated by a context for another user without the app - // having interact users permission - just for loading resources. - // For example, when adding widgets from a user profile to the - // home screen. Therefore, we register the receiver and content - // observer as the user the app is running as not the one the context is for. - context.registerReceiverAsUser(mBroadcastReceiver, android.os.Process.myUserHandle(), - filter, null, null); + context.registerReceiver(mBroadcastReceiver, filter); Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT); - context.getContentResolver().registerContentObserver(uri, true, mContentObserver, - UserHandle.myUserId()); + context.getContentResolver().registerContentObserver(uri, true, mContentObserver); } private void unregisterReceivers() { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 9de69f24d079..69d5f40802cc 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1653,10 +1653,8 @@ public class RemoteViews implements Parcelable, Filter { * * @param application The application whose content is shown by the views. * @param layoutId The id of the layout resource. - * - * @hide */ - protected RemoteViews(ApplicationInfo application, int layoutId) { + private RemoteViews(ApplicationInfo application, int layoutId) { mApplication = application; mLayoutId = layoutId; mBitmapCache = new BitmapCache(); diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java index acfc5431182d..4c5c71ded991 100644 --- a/core/java/android/widget/TextClock.java +++ b/core/java/android/widget/TextClock.java @@ -26,7 +26,6 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.SystemClock; -import android.os.UserHandle; import android.provider.Settings; import android.text.format.DateFormat; import android.util.AttributeSet; @@ -496,28 +495,12 @@ public class TextClock extends TextView { filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - // OK, this is gross but needed. This class is supported by the - // remote views mechanism and as a part of that the remote views - // can be inflated by a context for another user without the app - // having interact users permission - just for loading resources. - // For example, when adding widgets from a user profile to the - // home screen. Therefore, we register the receiver as the user - // the app is running as not the one the context is for. - getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(), - filter, null, getHandler()); + getContext().registerReceiver(mIntentReceiver, filter, null, getHandler()); } private void registerObserver() { final ContentResolver resolver = getContext().getContentResolver(); - // OK, this is gross but needed. This class is supported by the - // remote views mechanism and as a part of that the remote views - // can be inflated by a context for another user without the app - // having interact users permission - just for loading resources. - // For example, when adding widgets from a user profile to the - // home screen. Therefore, we register the content observer - // as the user the app is running as not the one the context is for. - resolver.registerContentObserver(Settings.System.CONTENT_URI, true, - mFormatChangeObserver, UserHandle.myUserId()); + resolver.registerContentObserver(Settings.System.CONTENT_URI, true, mFormatChangeObserver); } private void unregisterReceiver() { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 188a3e9970e2..3e1b674892a1 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -41,7 +41,6 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; -import android.os.UserHandle; import android.provider.Settings; import android.text.BoringLayout; import android.text.DynamicLayout; @@ -8338,15 +8337,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * to speak passwords. */ private boolean shouldSpeakPasswordsForAccessibility() { - // OK, this is gross but needed. This class is supported by the - // remote views mechanism and as a part of that the remote views - // can be inflated by a context for another user without the app - // having interact users permission - just for loading resources. - // For example, when adding widgets from a user profile to the - // home screen. Therefore, we access settings as user the app is - // running as not the one the context is for. - return (Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0, UserHandle.myUserId()) == 1); + return (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) == 1); } @Override diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml index 8069d13fa09d..edf6d9f4f859 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/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java index cd919a627f35..43922b8e3f6a 100644 --- a/graphics/java/android/graphics/drawable/Ripple.java +++ b/graphics/java/android/graphics/drawable/Ripple.java @@ -419,18 +419,22 @@ class Ripple { 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 @@ class Ripple { 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 4e68a60f36a4..80ecea32749b 100644 --- a/graphics/java/android/graphics/drawable/RippleBackground.java +++ b/graphics/java/android/graphics/drawable/RippleBackground.java @@ -326,6 +326,7 @@ class RippleBackground { private void endSoftwareAnimations() { if (mAnimOuterOpacity != null) { mAnimOuterOpacity.end(); + mAnimOuterOpacity = null; } } @@ -413,6 +414,7 @@ class RippleBackground { private void cancelSoftwareAnimations() { if (mAnimOuterOpacity != null) { mAnimOuterOpacity.cancel(); + mAnimOuterOpacity = null; } } diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk index 6e1402c4b2fd..c4a55d1e4eb9 100644 --- a/packages/PrintSpooler/Android.mk +++ b/packages/PrintSpooler/Android.mk @@ -23,6 +23,7 @@ LOCAL_SRC_FILES += src/com/android/printspooler/renderer/IPdfRenderer.aidl 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/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 7f7e5c36f552..2ef806f1babf 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -1863,7 +1863,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void deleteProviderLocked(Provider provider) { int N = provider.widgets.size(); - for (int i = 0; i < N; i++) { + for (int i = N - 1; i >= 0; i--) { Widget widget = provider.widgets.remove(i); // Call back with empty RemoteViews updateAppWidgetInstanceLocked(widget, null, false); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b116d762867c..023f627c55db 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 @@ public final class ActivityManagerService extends ActivityManagerNative if (resumed) { if (mUsageStatsService != null) { mUsageStatsService.reportEvent(component.realActivity, component.userId, - System.currentTimeMillis(), UsageEvents.Event.MOVE_TO_FOREGROUND); } synchronized (stats) { @@ -3224,7 +3223,6 @@ public final class ActivityManagerService extends ActivityManagerNative } else { if (mUsageStatsService != null) { mUsageStatsService.reportEvent(component.realActivity, component.userId, - System.currentTimeMillis(), UsageEvents.Event.MOVE_TO_BACKGROUND); } synchronized (stats) { @@ -15936,6 +15934,7 @@ public final class ActivityManagerService extends ActivityManagerNative 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/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 228c12058f7b..846efc075d5d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -10245,8 +10245,18 @@ public class PackageManagerService extends IPackageManager.Stub { if (bp != null) { // If the defining package is signed with our cert, it's okay. This // also includes the "updating the same package" case, of course. - if (compareSignatures(bp.packageSetting.signatures.mSignatures, - pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { + // "updating same package" could also involve key-rotation. + final boolean sigsOk; + if (!bp.sourcePackage.equals(pkg.packageName) + || !(bp.packageSetting instanceof PackageSetting) + || !bp.packageSetting.keySetData.isUsingUpgradeKeySets() + || ((PackageSetting) bp.packageSetting).sharedUser != null) { + sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures, + pkg.mSignatures) != PackageManager.SIGNATURE_MATCH; + } else { + sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg); + } + if (!sigsOk) { // If the owning package is the system itself, we log but allow // install to proceed; we fail the install on all other permission // redefinitions. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index cad277200730..3e6191cc3f7b 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // 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 dc036e251878..6c80a65ff007 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 @@ class IntervalStats { // 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 @@ class IntervalStats { 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 @@ class IntervalStats { } 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 e6ce0fe0b45b..37340a4e194c 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 @@ class UsageStatsDatabase { } /** - * 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 @@ class UsageStatsDatabase { 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 @@ class UsageStatsDatabase { UsageStatsXml.read(f, stats); if (beginTime < stats.endTime) { - results.addAll(stats.stats.values()); + combiner.combine(stats, false, results); } } return results; @@ -209,6 +229,10 @@ class UsageStatsDatabase { 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 0e8b427bfb2c..e77bf86e6764 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 @@ package com.android.server.usage; 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.IntentFilter; 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 @@ public class UsageStatsService extends SystemService implements * 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 @@ public class UsageStatsService extends SystemService implements /** * Called by the Binder stub. */ - UsageEvents queryEvents(int userId, long beginTime, long endTime) { - final long timeNow = System.currentTimeMillis(); + List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime, + long endTime) { + if (!validRange(beginTime, endTime)) { + return null; + } - // 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); + synchronized (mLock) { + UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId); + return service.queryConfigurationStats(bucketType, beginTime, endTime); + } + } - if (beginTime > endTime) { + /** + * Called by the Binder stub. + */ + UsageEvents queryEvents(int userId, long beginTime, long endTime) { + if (!validRange(beginTime, endTime)) { return null; } @@ -250,6 +260,11 @@ public class UsageStatsService extends SystemService implements } } + 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 @@ public class UsageStatsService extends SystemService implements } @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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 374429a8c01b..65299507d852 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 com.android.internal.util.XmlUtils; 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); - } - - if (parser.getEventType() != XmlPullParser.START_TAG || - !parser.getName().equals(EVENT_LOG_TAG)) { - return null; + final Configuration config = new Configuration(); + Configuration.readXmlAttrs(parser, config); + + 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 @@ final class UsageStatsXmlV1 { 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; + + if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) { + event.mConfiguration = new Configuration(); + Configuration.readXmlAttrs(parser, event.mConfiguration); + } + + if (statsOut.events == null) { + statsOut.events = new TimeSparseArray<>(); + } + statsOut.events.put(event.mTimeStamp, event); } - private static void writeUsageStats(FastXmlSerializer serializer, UsageStats stats) + private static void writeUsageStats(XmlSerializer xml, final 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); + 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(FastXmlSerializer serializer, UsageEvents.Event event) + private static void writeEvent(XmlSerializer xml, final UsageEvents.Event event) throws IOException { - serializer.startTag(null, EVENT_LOG_TAG); - serializer.attribute(null, PACKAGE_ATTR, event.mPackage); + xml.startTag(null, EVENT_LOG_TAG); + XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage); if (event.mClass != null) { - serializer.attribute(null, CLASS_ATTR, event.mClass); + 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); } - serializer.attribute(null, TYPE_ATTR, Integer.toString(event.getEventType())); - serializer.attribute(null, TIME_ATTR, Long.toString(event.getTimeStamp())); - serializer.endTag(null, EVENT_LOG_TAG); + + xml.endTag(null, EVENT_LOG_TAG); } /** @@ -142,6 +172,8 @@ final class UsageStatsXmlV1 { 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 @@ final class UsageStatsXmlV1 { 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); - } + 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; + } + + final String tag = parser.getName(); + switch (tag) { + case PACKAGE_TAG: + loadUsageStats(parser, statsOut); + break; - UsageEvents.Event event; - while ((event = readNextEvent(parser, statsOut)) != null) { - if (statsOut.events == null) { - statsOut.events = new TimeSparseArray<>(); + case CONFIGURATION_TAG: + loadConfigStats(parser, statsOut); + break; + + case EVENT_LOG_TAG: + loadEvent(parser, statsOut); + break; } - statsOut.events.put(event.getTimeStamp(), event); } } @@ -184,11 +224,15 @@ final class UsageStatsXmlV1 { 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 6951590f19da..7142a9999dda 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 @@ class UserUsageStatsService { 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); + } + + // Add the event to the daily list. + if (currentDailyStats.events == null) { + currentDailyStats.events = new TimeSparseArray<>(); } - mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events.put(event.getTimeStamp(), event); + 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 @@ class UserUsageStatsService { 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 @@ class UserUsageStatsService { + 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 @@ class UserUsageStatsService { // 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 @@ class UserUsageStatsService { 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 @@ class UserUsageStatsService { 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 @@ class UserUsageStatsService { 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/telecomm/java/android/telecomm/Log.java b/telecomm/java/android/telecomm/Log.java index b8dfb11146f0..446ae7560600 100644 --- a/telecomm/java/android/telecomm/Log.java +++ b/telecomm/java/android/telecomm/Log.java @@ -31,7 +31,7 @@ final public class Log { // Generic tag for all Telecomm Framework logging private static final String TAG = "TelecommFramework"; - public static final boolean FORCE_LOGGING = true; /* STOP SHIP if true */ + public static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */ public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG); public static final boolean INFO = isLoggable(android.util.Log.INFO); public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE); diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index acb97a047bf2..8af5e98dd7fc 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -164,14 +164,6 @@ public class PhoneNumberUtils return uri.getSchemeSpecificPart(); } - // TODO: We don't check for SecurityException here (requires - // CALL_PRIVILEGED permission). - if (scheme.equals("voicemail")) { - long subId = intent.getLongExtra(SUBSCRIPTION_KEY, - SubscriptionManager.getDefaultVoiceSubId()); - return TelephonyManager.getDefault().getCompleteVoiceMailNumber(subId); - } - if (context == null) { return null; } diff --git a/tests/UsageStatsTest/Android.mk b/tests/UsageStatsTest/Android.mk index 69fefeb5bddf..5f7467a301d2 100644 --- a/tests/UsageStatsTest/Android.mk +++ b/tests/UsageStatsTest/Android.mk @@ -6,6 +6,8 @@ LOCAL_MODULE_TAGS := tests # 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 000000000000..547de04ad728 --- /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 da50163c94ab..4f2bfe4222fb 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 5f62ad8032e7..31e7c38d3a03 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.app.usage.UsageStatsManager; 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 @@ public class UsageLogActivity extends ListActivity implements Runnable { 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 @@ public class UsageLogActivity extends ListActivity implements Runnable { @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 @@ public class UsageLogActivity extends ListActivity implements Runnable { } @Override - public Object getItem(int position) { + public UsageEvents.Event getItem(int position) { return mEvents.get(position); } @@ -95,41 +106,72 @@ public class UsageLogActivity extends ListActivity implements Runnable { } @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; } } |