Merge "Explicitly do not support SEARCH_SERVICE on UI_MODE_TYPE_WATCH"
diff --git a/api/current.txt b/api/current.txt
index e2d33d0..ee87fac 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38261,17 +38261,6 @@
method public byte[] transmit(byte[]) throws java.io.IOException;
}
- public abstract interface ISecureElementListener implements android.os.IInterface {
- method public abstract void serviceConnected() throws android.os.RemoteException;
- }
-
- public static abstract class ISecureElementListener.Stub extends android.os.Binder implements android.se.omapi.ISecureElementListener {
- ctor public ISecureElementListener.Stub();
- method public android.os.IBinder asBinder();
- method public static android.se.omapi.ISecureElementListener asInterface(android.os.IBinder);
- method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
- }
-
public class Reader {
method public void closeSessions();
method public java.lang.String getName();
@@ -38281,13 +38270,19 @@
}
public class SEService {
- ctor public SEService(android.content.Context, android.se.omapi.ISecureElementListener);
+ ctor public SEService(android.content.Context, android.se.omapi.SEService.SecureElementListener);
method public android.se.omapi.Reader[] getReaders();
method public java.lang.String getVersion();
method public boolean isConnected();
method public void shutdown();
}
+ public static abstract class SEService.SecureElementListener extends android.os.Binder {
+ ctor public SEService.SecureElementListener();
+ method public android.os.IBinder asBinder();
+ method public void serviceConnected();
+ }
+
public class Session {
method public void close();
method public void closeChannels();
diff --git a/api/test-current.txt b/api/test-current.txt
index 55e7926..d5b4311 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -732,6 +732,10 @@
field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
}
+ public class ServiceState implements android.os.Parcelable {
+ method public void setSystemAndNetworkId(int, int);
+ }
+
}
package android.telephony.mbms {
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 6b573e9..521ab4e 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -109,7 +109,8 @@
public static final int NOTIFICATION_SEEN = 10;
/**
- * An event type denoting a change in App Standby Bucket.
+ * An event type denoting a change in App Standby Bucket. Additional bucket information
+ * is contained in mBucketAndReason.
* @hide
*/
@SystemApi
@@ -180,11 +181,12 @@
public String[] mContentAnnotations;
/**
- * The app standby bucket assigned.
+ * The app standby bucket assigned and reason. Bucket is the high order 16 bits, reason
+ * is the low order 16 bits.
* Only present for {@link #STANDBY_BUCKET_CHANGED} event types
* {@hide}
*/
- public int mBucket;
+ public int mBucketAndReason;
/** @hide */
@EventFlags
@@ -205,7 +207,7 @@
mContentType = orig.mContentType;
mContentAnnotations = orig.mContentAnnotations;
mFlags = orig.mFlags;
- mBucket = orig.mBucket;
+ mBucketAndReason = orig.mBucketAndReason;
}
/**
@@ -268,7 +270,19 @@
*/
@SystemApi
public int getStandbyBucket() {
- return mBucket;
+ return (mBucketAndReason & 0xFFFF0000) >>> 16;
+ }
+
+ /**
+ * Returns the reason for the bucketing, if the event is of type
+ * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
+ * the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
+ * are sub-reasons for the main reason, such as REASON_SUB_USAGE_* when the main reason
+ * is REASON_MAIN_USAGE.
+ * @hide
+ */
+ public int getStandbyReason() {
+ return mBucketAndReason & 0x0000FFFF;
}
/** @hide */
@@ -428,7 +442,7 @@
p.writeStringArray(event.mContentAnnotations);
break;
case Event.STANDBY_BUCKET_CHANGED:
- p.writeInt(event.mBucket);
+ p.writeInt(event.mBucketAndReason);
break;
}
}
@@ -474,7 +488,7 @@
eventOut.mContentAnnotations = p.createStringArray();
break;
case Event.STANDBY_BUCKET_CHANGED:
- eventOut.mBucket = p.readInt();
+ eventOut.mBucketAndReason = p.readInt();
break;
}
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 9a0bb1d..5a57b06 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -132,24 +132,37 @@
@SystemApi
public static final int STANDBY_BUCKET_NEVER = 50;
- /** {@hide} Reason for bucketing -- default initial state */
- public static final String REASON_DEFAULT = "default";
+ /** @hide */
+ public static final int REASON_MAIN_MASK = 0xFF00;
+ /** @hide */
+ public static final int REASON_MAIN_DEFAULT = 0x0100;
+ /** @hide */
+ public static final int REASON_MAIN_TIMEOUT = 0x0200;
+ /** @hide */
+ public static final int REASON_MAIN_USAGE = 0x0300;
+ /** @hide */
+ public static final int REASON_MAIN_FORCED = 0x0400;
+ /** @hide */
+ public static final int REASON_MAIN_PREDICTED = 0x0500;
- /** {@hide} Reason for bucketing -- timeout */
- public static final String REASON_TIMEOUT = "timeout";
-
- /** {@hide} Reason for bucketing -- usage */
- public static final String REASON_USAGE = "usage";
-
- /** {@hide} Reason for bucketing -- forced by user / shell command */
- public static final String REASON_FORCED = "forced";
-
- /**
- * {@hide}
- * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
- * be appended.
- */
- public static final String REASON_PREDICTED = "predicted";
+ /** @hide */
+ public static final int REASON_SUB_MASK = 0x00FF;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_SYSTEM_INTERACTION = 0x0001;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_NOTIFICATION_SEEN = 0x0002;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_USER_INTERACTION = 0x0003;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_MOVE_TO_FOREGROUND = 0x0004;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_MOVE_TO_BACKGROUND = 0x0005;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_SYSTEM_UPDATE = 0x0006;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_ACTIVE_TIMEOUT = 0x0007;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_SYNC_ADAPTER = 0x0008;
/** @hide */
@IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
@@ -427,6 +440,55 @@
}
}
+ /** @hide */
+ public static String reasonToString(int standbyReason) {
+ StringBuilder sb = new StringBuilder();
+ switch (standbyReason & REASON_MAIN_MASK) {
+ case REASON_MAIN_DEFAULT:
+ sb.append("d");
+ break;
+ case REASON_MAIN_FORCED:
+ sb.append("f");
+ break;
+ case REASON_MAIN_PREDICTED:
+ sb.append("p");
+ break;
+ case REASON_MAIN_TIMEOUT:
+ sb.append("t");
+ break;
+ case REASON_MAIN_USAGE:
+ sb.append("u-");
+ switch (standbyReason & REASON_SUB_MASK) {
+ case REASON_SUB_USAGE_SYSTEM_INTERACTION:
+ sb.append("si");
+ break;
+ case REASON_SUB_USAGE_NOTIFICATION_SEEN:
+ sb.append("ns");
+ break;
+ case REASON_SUB_USAGE_USER_INTERACTION:
+ sb.append("ui");
+ break;
+ case REASON_SUB_USAGE_MOVE_TO_FOREGROUND:
+ sb.append("mf");
+ break;
+ case REASON_SUB_USAGE_MOVE_TO_BACKGROUND:
+ sb.append("mb");
+ break;
+ case REASON_SUB_USAGE_SYSTEM_UPDATE:
+ sb.append("su");
+ break;
+ case REASON_SUB_USAGE_ACTIVE_TIMEOUT:
+ sb.append("at");
+ break;
+ case REASON_SUB_USAGE_SYNC_ADAPTER:
+ sb.append("sa");
+ break;
+ }
+ break;
+ }
+ return sb.toString();
+ }
+
/**
* {@hide}
* Temporarily whitelist the specified app for a short duration. This is to allow an app
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 5d6a989..b62b1ee 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -139,7 +139,7 @@
/** Callback to inform listeners that the idle state has changed to a new bucket. */
public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
- boolean idle, int bucket);
+ boolean idle, int bucket, int reason);
/**
* Callback to inform listeners that the parole state has changed. This means apps are
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index eb30979..93690bf 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -18,13 +18,27 @@
import static android.content.ConfigurationProto.DENSITY_DPI;
import static android.content.ConfigurationProto.FONT_SCALE;
+import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.HDR_COLOR_MODE;
+import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.LOCALES;
+import static android.content.ConfigurationProto.MCC;
+import static android.content.ConfigurationProto.MNC;
+import static android.content.ConfigurationProto.NAVIGATION;
+import static android.content.ConfigurationProto.NAVIGATION_HIDDEN;
import static android.content.ConfigurationProto.ORIENTATION;
import static android.content.ConfigurationProto.SCREEN_HEIGHT_DP;
import static android.content.ConfigurationProto.SCREEN_LAYOUT;
import static android.content.ConfigurationProto.SCREEN_WIDTH_DP;
import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP;
+import static android.content.ConfigurationProto.TOUCHSCREEN;
import static android.content.ConfigurationProto.UI_MODE;
+import static android.content.ConfigurationProto.WIDE_COLOR_GAMUT;
import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX;
+import static android.content.ResourcesConfigurationProto.SCREEN_WIDTH_PX;
+import static android.content.ResourcesConfigurationProto.SDK_VERSION;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -38,6 +52,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.DisplayMetrics;
import android.util.proto.ProtoOutputStream;
import android.view.View;
@@ -1076,7 +1091,19 @@
public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
final long token = protoOutputStream.start(fieldId);
protoOutputStream.write(FONT_SCALE, fontScale);
+ protoOutputStream.write(MCC, mcc);
+ protoOutputStream.write(MNC, mnc);
+ mLocaleList.writeToProto(protoOutputStream, LOCALES);
protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
+ protoOutputStream.write(HDR_COLOR_MODE,
+ (colorMode & Configuration.COLOR_MODE_HDR_MASK) >> COLOR_MODE_HDR_SHIFT);
+ protoOutputStream.write(WIDE_COLOR_GAMUT,
+ colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK);
+ protoOutputStream.write(TOUCHSCREEN, touchscreen);
+ protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden);
+ protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden);
+ protoOutputStream.write(NAVIGATION, navigation);
+ protoOutputStream.write(NAVIGATION_HIDDEN, navigationHidden);
protoOutputStream.write(ORIENTATION, orientation);
protoOutputStream.write(UI_MODE, uiMode);
protoOutputStream.write(SCREEN_WIDTH_DP, screenWidthDp);
@@ -1088,6 +1115,36 @@
}
/**
+ * Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
+ * stream.
+ *
+ * @param protoOutputStream Stream to write the Configuration object to.
+ * @param fieldId Field Id of the Configuration as defined in the parent message
+ * @param metrics Current display information
+ * @hide
+ */
+ public void writeResConfigToProto(ProtoOutputStream protoOutputStream, long fieldId,
+ DisplayMetrics metrics) {
+ final int width, height;
+ if (metrics.widthPixels >= metrics.heightPixels) {
+ width = metrics.widthPixels;
+ height = metrics.heightPixels;
+ } else {
+ //noinspection SuspiciousNameCombination
+ width = metrics.heightPixels;
+ //noinspection SuspiciousNameCombination
+ height = metrics.widthPixels;
+ }
+
+ final long token = protoOutputStream.start(fieldId);
+ writeToProto(protoOutputStream, CONFIGURATION);
+ protoOutputStream.write(SDK_VERSION, Build.VERSION.RESOURCES_SDK_INT);
+ protoOutputStream.write(SCREEN_WIDTH_PX, width);
+ protoOutputStream.write(SCREEN_HEIGHT_PX, height);
+ protoOutputStream.end(token);
+ }
+
+ /**
* Convert the UI mode to a human readable format.
* @hide
*/
@@ -1925,11 +1982,21 @@
/**
* Returns a string representation of the configuration that can be parsed
- * by build tools (like AAPT).
+ * by build tools (like AAPT), without display metrics included
*
* @hide
*/
public static String resourceQualifierString(Configuration config) {
+ return resourceQualifierString(config, null);
+ }
+
+ /**
+ * Returns a string representation of the configuration that can be parsed
+ * by build tools (like AAPT).
+ *
+ * @hide
+ */
+ public static String resourceQualifierString(Configuration config, DisplayMetrics metrics) {
ArrayList<String> parts = new ArrayList<String>();
if (config.mcc != 0) {
@@ -2177,6 +2244,20 @@
break;
}
+ if (metrics != null) {
+ final int width, height;
+ if (metrics.widthPixels >= metrics.heightPixels) {
+ width = metrics.widthPixels;
+ height = metrics.heightPixels;
+ } else {
+ //noinspection SuspiciousNameCombination
+ width = metrics.heightPixels;
+ //noinspection SuspiciousNameCombination
+ height = metrics.widthPixels;
+ }
+ parts.add(width + "x" + height);
+ }
+
parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
return TextUtils.join("-", parts);
}
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index ca9cbec..87e1b7d 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -20,7 +20,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.content.LocaleProto;
import android.icu.util.ULocale;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -140,6 +142,25 @@
}
/**
+ * Helper to write LocaleList to a protocol buffer output stream. Assumes the parent
+ * protobuf has declared the locale as repeated.
+ *
+ * @param protoOutputStream Stream to write the locale to.
+ * @param fieldId Field Id of the Locale as defined in the parent message.
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ for (int i = 0; i < mList.length; i++) {
+ final Locale locale = mList[i];
+ final long token = protoOutputStream.start(fieldId);
+ protoOutputStream.write(LocaleProto.LANGUAGE, locale.getLanguage());
+ protoOutputStream.write(LocaleProto.COUNTRY, locale.getCountry());
+ protoOutputStream.write(LocaleProto.VARIANT, locale.getVariant());
+ protoOutputStream.end(token);
+ }
+ }
+
+ /**
* Retrieves a String representation of the language tags in this list.
*/
@NonNull
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/core/java/android/se/omapi/ISecureElementListener.aidl
index 3a99d63..e0c6e04 100644
--- a/core/java/android/se/omapi/ISecureElementListener.aidl
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -21,6 +21,7 @@
/**
* Interface to receive call-backs when the service is connected.
+ * @hide
*/
interface ISecureElementListener {
/**
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index b8937e6..d59e86a 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -59,6 +59,21 @@
*/
public static final int NO_SUCH_ELEMENT_ERROR = 2;
+ /**
+ * Interface to send call-backs to the application when the service is connected.
+ */
+ public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ /**
+ * Called by the framework when the service is connected.
+ */
+ public void serviceConnected() {};
+ }
+
private static final String TAG = "OMAPI.SEService";
private final Object mLock = new Object();
@@ -98,9 +113,9 @@
* the context of the calling application. Cannot be
* <code>null</code>.
* @param listener
- * a ISecureElementListener object. Can be <code>null</code>.
+ * a SecureElementListener object. Can be <code>null</code>.
*/
- public SEService(Context context, ISecureElementListener listener) {
+ public SEService(Context context, SecureElementListener listener) {
if (context == null) {
throw new NullPointerException("context must not be null");
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9b4ea33..d837518 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -137,7 +137,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 175 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 176 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -14602,6 +14602,7 @@
u.mJobsFreshnessTimeMs.writeSummaryFromParcelLocked(out);
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
if (u.mJobsFreshnessBuckets[i] != null) {
+ out.writeInt(1);
u.mJobsFreshnessBuckets[i].writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 7178ec7..46667d1 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
import android.os.Process;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.util.Slog;
@@ -69,6 +70,7 @@
boolean wakeup_sources;
final long startTime = SystemClock.uptimeMillis();
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
try {
FileInputStream is;
try {
@@ -90,6 +92,8 @@
} catch (java.io.IOException e) {
Slog.wtf(TAG, "failed to read kernel wakelocks", e);
return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
}
final long readTime = SystemClock.uptimeMillis() - startTime;
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
index a62d56c..834ecde 100644
--- a/core/proto/android/content/configuration.proto
+++ b/core/proto/android/content/configuration.proto
@@ -25,7 +25,7 @@
import "frameworks/base/libs/incident/proto/android/privacy.proto";
/**
- * An android resource configuration.
+ * An android Configuration object.
*/
message ConfigurationProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -35,17 +35,65 @@
optional uint32 mnc = 3;
repeated LocaleProto locales = 4;
optional uint32 screen_layout = 5;
- optional uint32 touchscreen = 6;
- optional uint32 keyboard_hidden = 7;
- optional uint32 hard_keyboard_hidden = 8;
- optional uint32 navigation = 9;
- optional uint32 navigation_hidden = 10;
- optional uint32 orientation = 11;
- optional uint32 ui_mode = 12;
- optional uint32 screen_width_dp = 13;
- optional uint32 screen_height_dp = 14;
- optional uint32 smallest_screen_width_dp = 15;
- optional uint32 density_dpi = 16;
- optional .android.app.WindowConfigurationProto window_configuration = 17;
+ optional uint32 hdr_color_mode = 6;
+ optional uint32 wide_color_gamut = 7;
+ optional uint32 touchscreen = 8;
+ optional uint32 keyboard_hidden = 9;
+ optional uint32 hard_keyboard_hidden = 10;
+ optional uint32 navigation = 11;
+ optional uint32 navigation_hidden = 12;
+ optional uint32 orientation = 13;
+ optional uint32 ui_mode = 14;
+ optional uint32 screen_width_dp = 15;
+ optional uint32 screen_height_dp = 16;
+ optional uint32 smallest_screen_width_dp = 17;
+ optional uint32 density_dpi = 18;
+ optional .android.app.WindowConfigurationProto window_configuration = 19;
}
+/**
+ * All current configuration data used to select resources.
+ */
+message ResourcesConfigurationProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ required ConfigurationProto configuration = 1;
+
+ optional uint32 sdk_version = 2;
+ optional uint32 screen_width_px = 3;
+ optional uint32 screen_height_px = 4;
+}
+
+/**
+ * Overall device configuration data.
+ */
+message DeviceConfigurationProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional uint32 stable_screen_width_px = 1;
+ optional uint32 stable_screen_height_px = 2;
+ optional uint32 stable_density_dpi = 3;
+
+ optional uint64 total_ram = 4;
+ optional bool low_ram = 5;
+ optional uint32 max_cores = 6;
+ optional bool has_secure_screen_lock = 7;
+
+ optional uint32 opengl_version = 8;
+ repeated string opengl_extensions = 9;
+
+ repeated string shared_libraries = 10;
+ repeated string features = 11;
+ repeated string cpu_architectures = 12;
+}
+
+/**
+ * All current configuration data device is running with, everything used
+ * to filter and target apps.
+ */
+message GlobalConfigurationProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional ResourcesConfigurationProto resources = 1;
+ optional DeviceConfigurationProto device = 2;
+}
diff --git a/core/res/res/drawable-watch/sym_def_app_icon.xml b/core/res/res/drawable-watch/sym_def_app_icon.xml
index 2720bfa..6945256 100644
--- a/core/res/res/drawable-watch/sym_def_app_icon.xml
+++ b/core/res/res/drawable-watch/sym_def_app_icon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
- <background android:drawable="@color/accent_device_default_dark" />
+ <background android:drawable="@drawable/sym_def_app_icon_background" />
<foreground android:drawable="@mipmap/sym_def_app_icon_foreground" />
</adaptive-icon>
diff --git a/core/res/res/drawable-watch/sym_def_app_icon_background.xml b/core/res/res/drawable-watch/sym_def_app_icon_background.xml
new file mode 100644
index 0000000..6d6352f
--- /dev/null
+++ b/core/res/res/drawable-watch/sym_def_app_icon_background.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#2374CE"/>
+</shape>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index 525d493..346aad6c 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -43,6 +43,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
+ android:layout_weight="1"
android:paddingStart="@dimen/notification_content_margin_start"
android:paddingEnd="@dimen/notification_content_margin_end"
android:clipToPadding="false"
@@ -75,19 +76,19 @@
android:maxLines="3"
/>
</LinearLayout>
+ <FrameLayout android:id="@+id/actions_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom">
+ <com.android.internal.widget.NotificationActionListLayout
+ android:id="@+id/actions"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_action_list_height"
+ android:paddingEnd="4dp"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:visibility="gone"
+ />
+ </FrameLayout>
</LinearLayout>
- <FrameLayout android:id="@+id/actions_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom">
- <com.android.internal.widget.NotificationActionListLayout
- android:id="@+id/actions"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_action_list_height"
- android:paddingEnd="4dp"
- android:orientation="horizontal"
- android:gravity="center"
- android:visibility="gone"
- />
- </FrameLayout>
</FrameLayout>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 3cda9c9..784c714 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -444,6 +444,7 @@
private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
+ mDeviceManager.onActiveDeviceChanged(activeDevice, bluetoothProfile);
synchronized (mCallbacks) {
for (BluetoothCallback callback : mCallbacks) {
callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e1ebbc4..f6ec6a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -27,6 +27,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.bluetooth.BluetoothAdapter;
+import android.support.annotation.VisibleForTesting;
import com.android.settingslib.R;
@@ -461,12 +462,12 @@
}
/**
- * Set the device status as active or non-active per Bluetooth profile.
+ * Update the device status as active or non-active per Bluetooth profile.
*
* @param isActive true if the device is active
* @param bluetoothProfile the Bluetooth profile
*/
- public void setActiveDevice(boolean isActive, int bluetoothProfile) {
+ public void onActiveDeviceChanged(boolean isActive, int bluetoothProfile) {
boolean changed = false;
switch (bluetoothProfile) {
case BluetoothProfile.A2DP:
@@ -478,7 +479,7 @@
mIsActiveDeviceHeadset = isActive;
break;
default:
- Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
+ Log.w(TAG, "onActiveDeviceChanged: unknown profile " + bluetoothProfile +
" isActive " + isActive);
break;
}
@@ -487,6 +488,26 @@
}
}
+ /**
+ * Get the device status as active or non-active per Bluetooth profile.
+ *
+ * @param bluetoothProfile the Bluetooth profile
+ * @return true if the device is active
+ */
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public boolean isActiveDevice(int bluetoothProfile) {
+ switch (bluetoothProfile) {
+ case BluetoothProfile.A2DP:
+ return mIsActiveDeviceA2dp;
+ case BluetoothProfile.HEADSET:
+ return mIsActiveDeviceHeadset;
+ default:
+ Log.w(TAG, "getActiveDevice: unknown profile " + bluetoothProfile);
+ break;
+ }
+ return false;
+ }
+
void setRssi(short rssi) {
if (mRssi != rssi) {
mRssi = rssi;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index c3ff617..a8e0039 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
/**
* CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
@@ -167,6 +168,15 @@
}
}
}
+
+ public synchronized void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ int bluetoothProfile) {
+ for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+ boolean isActive = Objects.equals(cachedDevice, activeDevice);
+ cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
+ }
+ }
+
private void log(String msg) {
if (DEBUG) {
Log.d(TAG, msg);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
new file mode 100644
index 0000000..d6b2006
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.Collection;
+
+@RunWith(RobolectricTestRunner.class)
+public class CachedBluetoothDeviceManagerTest {
+ private final static String DEVICE_NAME_1 = "TestName_1";
+ private final static String DEVICE_NAME_2 = "TestName_2";
+ private final static String DEVICE_ALIAS_1 = "TestAlias_1";
+ private final static String DEVICE_ALIAS_2 = "TestAlias_2";
+ private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
+ private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
+ private final BluetoothClass DEVICE_CLASS_1 =
+ new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+ private final BluetoothClass DEVICE_CLASS_2 =
+ new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+ @Mock
+ private LocalBluetoothAdapter mLocalAdapter;
+ @Mock
+ private LocalBluetoothProfileManager mLocalProfileManager;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private BluetoothEventManager mBluetoothEventManager;
+ @Mock
+ private HeadsetProfile mHfpProfile;
+ @Mock
+ private A2dpProfile mA2dpProfile;
+ @Mock
+ private PanProfile mPanProfile;
+ @Mock
+ private BluetoothDevice mDevice1;
+ @Mock
+ private BluetoothDevice mDevice2;
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
+ private Context mContext;
+ private String[] mActiveDeviceStringsArray;
+ private String mActiveDeviceStringNone;
+ private String mActiveDeviceStringAll;
+ private String mActiveDeviceStringMedia;
+ private String mActiveDeviceStringPhone;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
+ when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
+ when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
+ when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
+ when(mDevice1.getAliasName()).thenReturn(DEVICE_ALIAS_1);
+ when(mDevice2.getAliasName()).thenReturn(DEVICE_ALIAS_2);
+ when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS_1);
+ when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
+
+ when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
+ when(mLocalAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
+ when(mHfpProfile.isProfileReady()).thenReturn(true);
+ when(mA2dpProfile.isProfileReady()).thenReturn(true);
+ when(mPanProfile.isProfileReady()).thenReturn(true);
+ mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
+ }
+
+ /**
+ * Test to verify addDevice().
+ */
+ @Test
+ public void testAddDevice_validCachedDevices_devicesAdded() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice2);
+ assertThat(cachedDevice2).isNotNull();
+
+ Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).contains(cachedDevice2);
+
+ assertThat(mCachedDeviceManager.findDevice(mDevice1)).isEqualTo(cachedDevice1);
+ assertThat(mCachedDeviceManager.findDevice(mDevice2)).isEqualTo(cachedDevice2);
+ }
+
+ /**
+ * Test to verify getName().
+ */
+ @Test
+ public void testGetName_validCachedDevice_nameFound() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
+ }
+
+ /**
+ * Test to verify onDeviceNameUpdated().
+ */
+ @Test
+ public void testOnDeviceNameUpdated_validName_nameUpdated() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1);
+
+ final String newAliasName = "NewAliasName";
+ when(mDevice1.getAliasName()).thenReturn(newAliasName);
+ mCachedDeviceManager.onDeviceNameUpdated(mDevice1);
+ assertThat(cachedDevice1.getName()).isEqualTo(newAliasName);
+ }
+
+ /**
+ * Test to verify clearNonBondedDevices().
+ */
+ @Test
+ public void testClearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice2);
+ assertThat(cachedDevice2).isNotNull();
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+ mCachedDeviceManager.clearNonBondedDevices();
+ Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).contains(cachedDevice2);
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ mCachedDeviceManager.clearNonBondedDevices();
+ devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).doesNotContain(cachedDevice2);
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ mCachedDeviceManager.clearNonBondedDevices();
+ devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).doesNotContain(cachedDevice1);
+ assertThat(devices).doesNotContain(cachedDevice2);
+ }
+
+ /**
+ * Test to verify onBtClassChanged().
+ */
+ @Test
+ public void testOnBtClassChanged_validBtClass_classChanged() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ assertThat(cachedDevice1.getBtClass()).isEqualTo(DEVICE_CLASS_1);
+
+ final BluetoothClass newBluetoothClass = DEVICE_CLASS_2;
+ when(mDevice1.getBluetoothClass()).thenReturn(newBluetoothClass);
+ mCachedDeviceManager.onBtClassChanged(mDevice1);
+ assertThat(cachedDevice1.getBtClass()).isEqualTo(newBluetoothClass);
+ }
+
+ /**
+ * Test to verify onDeviceDisappeared().
+ */
+ @Test
+ public void testOnDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isFalse();
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isTrue();
+ }
+
+ /**
+ * Test to verify onActiveDeviceChanged().
+ */
+ @Test
+ public void testOnActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice2);
+ assertThat(cachedDevice2).isNotNull();
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ // Connect both devices for A2DP and HFP
+ cachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ cachedDevice2.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ cachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ cachedDevice2.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+
+ // Verify that both devices are connected and none is Active
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+
+ // The first device is active for A2DP, the second device is active for HFP
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.A2DP);
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+
+ // The first device is active for A2DP and HFP
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.HEADSET);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+
+ // The second device is active for A2DP and HFP
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.A2DP);
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+
+ // No active device for A2DP
+ mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.A2DP);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+
+ // No active device for HFP
+ mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 92c68e6..0775727 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -147,7 +147,7 @@
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set device as Active for A2DP and test connection state summary
- mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
// Test with battery level
@@ -163,7 +163,7 @@
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set A2DP profile to be connected, Active and test connection state summary
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
// Set A2DP profile to be disconnected and test connection state summary
@@ -179,7 +179,7 @@
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set device as Active for HFP and test connection state summary
- mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
// Test with battery level
@@ -195,7 +195,7 @@
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set HFP profile to be connected, Active and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
// Set HFP profile to be disconnected and test connection state summary
@@ -212,8 +212,8 @@
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set device as Active for A2DP and HFP and test connection state summary
- mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
- mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
// Test with battery level
@@ -222,16 +222,16 @@
"Connected, battery 10%, active");
// Disconnect A2DP only and test connection state summary
- mCachedDevice.setActiveDevice(false, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"Connected (no media), battery 10%, active(phone)");
// Disconnect HFP only and test connection state summary
- mCachedDevice.setActiveDevice(false, BluetoothProfile.HEADSET);
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"Connected (no phone), battery 10%, active(media)");
@@ -240,8 +240,8 @@
// Set A2DP and HFP profiles to be connected, Active and test connection state summary
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
- mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
// Set A2DP and HFP profiles to be disconnected and test connection state summary
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 4ffe5fe..933c952 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4985,7 +4985,7 @@
notificationKey)) {
// Show work challenge, do not run PendingIntent and
// remove notification
- collapsePanel();
+ collapseOnMainThread();
return;
}
}
@@ -5026,11 +5026,7 @@
}
}
if (shouldCollapse()) {
- if (Looper.getMainLooper().isCurrentThread()) {
- collapsePanel();
- } else {
- mStackScroller.post(this::collapsePanel);
- }
+ collapseOnMainThread();
}
try {
@@ -5058,6 +5054,14 @@
}, afterKeyguardGone);
}
+ private void collapseOnMainThread() {
+ if (Looper.getMainLooper().isCurrentThread()) {
+ collapsePanel();
+ } else {
+ mStackScroller.post(this::collapsePanel);
+ }
+ }
+
private boolean shouldCollapse() {
return mState != StatusBarState.SHADE || !mActivityLaunchAnimator.isAnimationPending();
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9cd3621..c93f405 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -3860,7 +3860,7 @@
final class AppStandbyTracker extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
- boolean idle, int bucket) {
+ boolean idle, int bucket, int reason) {
if (DEBUG_STANDBY) {
Slog.d(TAG, "Package " + packageName + " for user " + userId + " now in bucket " +
bucket);
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index fc4d463..a6b71b7 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -678,7 +678,7 @@
final class StandbyTracker extends AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
- int bucket) {
+ int bucket, int reason) {
if (DEBUG) {
Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId
+ (idle ? " idle" : " active") + " " + bucket);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 0d6d2bd..6550d06 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -85,7 +85,7 @@
private static final int DEFAULT_POWER_CHECK_MAX_CPU_3 = 10;
private static final int DEFAULT_POWER_CHECK_MAX_CPU_4 = 2;
private static final long DEFAULT_SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
- private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 24*60*60*1000L;
+ private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 2*60*60*1000L;
private static final long DEFAULT_SERVICE_RESTART_DURATION = 1*1000;
private static final long DEFAULT_SERVICE_RESET_RUN_DURATION = 60*1000;
private static final int DEFAULT_SERVICE_RESTART_DURATION_FACTOR = 4;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 07e27bc..3b2a22d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -23,6 +23,7 @@
import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.IUidObserver;
+import android.app.KeyguardManager;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.usage.AppStandbyInfo;
@@ -32,16 +33,24 @@
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DeviceConfigurationProto;
+import android.content.GlobalConfigurationProto;
import android.content.IIntentReceiver;
import android.content.Intent;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.content.pm.SharedLibraryInfo;
import android.content.pm.UserInfo;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -57,8 +66,11 @@
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.DisplayMetrics;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import com.android.internal.util.HexDump;
+import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
import java.io.BufferedReader;
@@ -75,6 +87,11 @@
import java.util.List;
import java.util.Map;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
@@ -1835,17 +1852,112 @@
}
}
- int runGetConfig(PrintWriter pw) throws RemoteException {
- int days = 14;
- String option = getNextOption();
- if (option != null) {
- if (!option.equals("--days")) {
- throw new IllegalArgumentException("unrecognized option " + option);
- }
+ private void writeDeviceConfig(ProtoOutputStream protoOutputStream, long fieldId,
+ PrintWriter pw, Configuration config, DisplayManager dm) {
+ Point stableSize = dm.getStableDisplaySize();
+ long token = -1;
+ if (protoOutputStream != null) {
+ token = protoOutputStream.start(fieldId);
+ protoOutputStream.write(DeviceConfigurationProto.STABLE_SCREEN_WIDTH_PX, stableSize.x);
+ protoOutputStream.write(DeviceConfigurationProto.STABLE_SCREEN_HEIGHT_PX, stableSize.y);
+ protoOutputStream.write(DeviceConfigurationProto.STABLE_DENSITY_DPI,
+ DisplayMetrics.DENSITY_DEVICE_STABLE);
+ }
+ if (pw != null) {
+ pw.print("stable-width-px: "); pw.println(stableSize.x);
+ pw.print("stable-height-px: "); pw.println(stableSize.y);
+ pw.print("stable-density-dpi: "); pw.println(DisplayMetrics.DENSITY_DEVICE_STABLE);
+ }
- days = Integer.parseInt(getNextArgRequired());
- if (days <= 0) {
- throw new IllegalArgumentException("--days must be a positive integer");
+ MemInfoReader memreader = new MemInfoReader();
+ memreader.readMemInfo();
+ KeyguardManager kgm = mInternal.mContext.getSystemService(KeyguardManager.class);
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.TOTAL_RAM, memreader.getTotalSize());
+ protoOutputStream.write(DeviceConfigurationProto.LOW_RAM,
+ ActivityManager.isLowRamDeviceStatic());
+ protoOutputStream.write(DeviceConfigurationProto.MAX_CORES,
+ Runtime.getRuntime().availableProcessors());
+ protoOutputStream.write(DeviceConfigurationProto.HAS_SECURE_SCREEN_LOCK,
+ kgm.isDeviceSecure());
+ }
+ if (pw != null) {
+ pw.print("total-ram: "); pw.println(memreader.getTotalSize());
+ pw.print("low-ram: "); pw.println(ActivityManager.isLowRamDeviceStatic());
+ pw.print("max-cores: "); pw.println(Runtime.getRuntime().availableProcessors());
+ pw.print("has-secure-screen-lock: "); pw.println(kgm.isDeviceSecure());
+ }
+
+ ConfigurationInfo configInfo = mInternal.getDeviceConfigurationInfo();
+ if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
+ configInfo.reqGlEsVersion);
+ }
+ if (pw != null) {
+ pw.print("opengl-version: 0x");
+ pw.println(Integer.toHexString(configInfo.reqGlEsVersion));
+ }
+ }
+
+ /*
+ GL10 gl = ((GL10)((EGL10)EGLContext.getEGL()).eglGetCurrentContext().getGL());
+ protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
+ gl.glGetString(GL10.GL_VERSION));
+ String glExtensions = gl.glGetString(GL10.GL_EXTENSIONS);
+ for (String ext : glExtensions.split(" ")) {
+ protoOutputStream.write(DeviceConfigurationProto.OPENGL_EXTENSIONS, ext);
+ }
+ */
+
+ PackageManager pm = mInternal.mContext.getPackageManager();
+ List<SharedLibraryInfo> slibs = pm.getSharedLibraries(0);
+ for (int i = 0; i < slibs.size(); i++) {
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.SHARED_LIBRARIES,
+ slibs.get(i).getName());
+ }
+ if (pw != null) {
+ pw.print("shared-libraries: "); pw.println(slibs.get(i).getName());
+ }
+ }
+
+ FeatureInfo[] features = pm.getSystemAvailableFeatures();
+ for (int i = 0; i < features.length; i++) {
+ if (features[i].name != null) {
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.FEATURES, features[i].name);
+ }
+ if (pw != null) {
+ pw.print("features: "); pw.println(features[i].name);
+ }
+ }
+ }
+
+ if (protoOutputStream != null) {
+ protoOutputStream.end(token);
+ }
+ }
+
+ int runGetConfig(PrintWriter pw) throws RemoteException {
+ int days = -1;
+ boolean asProto = false;
+ boolean inclDevice = false;
+
+ String opt;
+ while ((opt=getNextOption()) != null) {
+ if (opt.equals("--days")) {
+ days = Integer.parseInt(getNextArgRequired());
+ if (days <= 0) {
+ throw new IllegalArgumentException("--days must be a positive integer");
+ }
+ } else if (opt.equals("--proto")) {
+ asProto = true;
+ } else if (opt.equals("--device")) {
+ inclDevice = true;
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
}
}
@@ -1855,18 +1967,38 @@
return -1;
}
- pw.println("config: " + Configuration.resourceQualifierString(config));
- pw.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
+ DisplayManager dm = mInternal.mContext.getSystemService(DisplayManager.class);
+ Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
- final List<Configuration> recentConfigs = getRecentConfigurations(days);
- final int recentConfigSize = recentConfigs.size();
- if (recentConfigSize > 0) {
- pw.println("recentConfigs:");
- }
+ if (asProto) {
+ final ProtoOutputStream proto = new ProtoOutputStream(getOutFileDescriptor());
+ config.writeResConfigToProto(proto, GlobalConfigurationProto.RESOURCES, metrics);
+ if (inclDevice) {
+ writeDeviceConfig(proto, GlobalConfigurationProto.DEVICE, null, config, dm);
+ }
+ proto.flush();
- for (int i = 0; i < recentConfigSize; i++) {
- pw.println(" config: " + Configuration.resourceQualifierString(
- recentConfigs.get(i)));
+ } else {
+ pw.println("config: " + Configuration.resourceQualifierString(config, metrics));
+ pw.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
+ if (inclDevice) {
+ writeDeviceConfig(null, -1, pw, config, dm);
+ }
+
+ if (days >= 0) {
+ final List<Configuration> recentConfigs = getRecentConfigurations(days);
+ final int recentConfigSize = recentConfigs.size();
+ if (recentConfigSize > 0) {
+ pw.println("recentConfigs:");
+ for (int i = 0; i < recentConfigSize; i++) {
+ pw.println(" config: " + Configuration.resourceQualifierString(
+ recentConfigs.get(i)));
+ }
+ }
+ }
+
}
return 0;
}
@@ -2729,8 +2861,11 @@
pw.println(" Gets the process state of an app given its <UID>.");
pw.println(" attach-agent <PROCESS> <FILE>");
pw.println(" Attach an agent to the specified <PROCESS>, which may be either a process name or a PID.");
- pw.println(" get-config");
- pw.println(" Rtrieve the configuration and any recent configurations of the device.");
+ pw.println(" get-config [--days N] [--device] [--proto]");
+ pw.println(" Retrieve the configuration and any recent configurations of the device.");
+ pw.println(" --days: also return last N days of configurations that have been seen.");
+ pw.println(" --device: also output global device configuration info.");
+ pw.println(" --proto: return result as a proto; does not include --days info.");
pw.println(" supports-multiwindow");
pw.println(" Returns true if the device supports multiwindow.");
pw.println(" supports-split-screen-multi-window");
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 740866c..017fada 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -2240,7 +2240,7 @@
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
- boolean idle, int bucket) {
+ boolean idle, int bucket, int reason) {
final int uid = mLocalPM.getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
if (uid < 0) {
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index bd8fe28..ed29a4c 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -186,7 +186,8 @@
private final class AppIdleStateChangeListener
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
+ int reason) {
boolean changed = false;
synchronized (mLock) {
if (mAppIdleParoleOn) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f29e0bb..ab55553 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3947,7 +3947,8 @@
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
+ int reason) {
try {
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
diff --git a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
index 90db2a3..796d364 100644
--- a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
@@ -15,6 +15,9 @@
*/
package com.android.server;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+
import static com.android.server.AppStateTracker.TARGET_OP;
import static org.junit.Assert.assertEquals;
@@ -612,7 +615,7 @@
// Exempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -624,7 +627,7 @@
// Exempt package 1 on user-0.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -632,7 +635,7 @@
// Unexempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
- UsageStatsManager.STANDBY_BUCKET_ACTIVE);
+ UsageStatsManager.STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -644,9 +647,9 @@
mPowerSaveObserver.accept(getPowerSaveState());
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 0, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
setAppOps(UID_1, PACKAGE_1, true);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 7b06648..36504ac 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,10 +16,13 @@
package com.android.server.usage;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -92,18 +95,18 @@
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 1000, STANDBY_BUCKET_ACTIVE,
- UsageStatsManager.REASON_USAGE);
+ REASON_MAIN_USAGE);
// ACTIVE means not idle
assertFalse(aih.isIdle(PACKAGE_1, USER_ID, 2000));
aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
- UsageStatsManager.REASON_USAGE);
+ REASON_MAIN_USAGE);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
- REASON_TIMEOUT);
+ REASON_MAIN_TIMEOUT);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE);
- assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_TIMEOUT);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
// RARE is considered idle
assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
@@ -115,7 +118,7 @@
aih = new AppIdleHistory(mStorageDir, 4000);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
- assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_TIMEOUT);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
@@ -133,4 +136,18 @@
assertEquals(1000, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 7000));
assertEquals(5000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 7000));
}
+
+ public void testReason() throws Exception {
+ AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+ aih.reportUsage(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0);
+ assertEquals(REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND,
+ aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
+ aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 4000, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_TIMEOUT);
+ aih.writeAppIdleTimes(USER_ID);
+
+ aih = new AppIdleHistory(mStorageDir, 5000);
+ assertEquals(REASON_MAIN_TIMEOUT, aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000));
+ }
}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index cbbdca6..edf1f74 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -18,11 +18,11 @@
import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -410,11 +410,11 @@
setChargingState(mController, false);
// Set it to timeout or usage, so that prediction can override it
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
- REASON_TIMEOUT, 1 * HOUR_MS);
+ REASON_MAIN_TIMEOUT, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED + ":CTS", 1 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
// Fast forward 12 hours
@@ -440,28 +440,28 @@
setChargingState(mController, false);
// Can force to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_FORCED, 1 * HOUR_MS);
+ REASON_MAIN_FORCED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
// Prediction can't override FORCED reason
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_FORCED, 1 * HOUR_MS);
+ REASON_MAIN_FORCED, 1 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
- REASON_PREDICTED, 1 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
// Prediction can't override NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_DEFAULT, 2 * HOUR_MS);
+ REASON_MAIN_DEFAULT, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED, 2 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 2 * HOUR_MS);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
// Prediction can't set to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_USAGE, 2 * HOUR_MS);
+ REASON_MAIN_USAGE, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_PREDICTED, 2 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 2 * HOUR_MS);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
}
@@ -474,7 +474,7 @@
mInjector.mElapsedRealtime = 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// bucketing works after timeout
@@ -483,7 +483,7 @@
assertBucket(STANDBY_BUCKET_WORKING_SET);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -498,15 +498,15 @@
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
- REASON_PREDICTED, 1000);
+ REASON_MAIN_PREDICTED, 1000);
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
+ REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
+ REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -527,18 +527,18 @@
// Still in ACTIVE after first USER_INTERACTION times out
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Both timed out, so NOTIFICATION_SEEN timeout should be effective
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_RARE);
}
@@ -561,7 +561,7 @@
// Predict to ACTIVE
mInjector.mElapsedRealtime += 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// CheckIdleStates should not change the prediction
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 8e5a418..fd28b65 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,10 +16,12 @@
package com.android.server.usage;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -112,8 +114,10 @@
// Standby bucket
@UsageStatsManager.StandbyBuckets
int currentBucket;
- // Reason for setting the standby bucket. TODO: Switch to int.
- String bucketingReason;
+ // Reason for setting the standby bucket. The value here is a combination of
+ // one of UsageStatsManager.REASON_MAIN_* and one (or none) of
+ // UsageStatsManager.REASON_SUB_*. Also see REASON_MAIN_MASK and REASON_SUB_MASK.
+ int bucketingReason;
// In-memory only, last bucket for which the listeners were informed
int lastInformedBucket;
// The last time a job was run for this app, using elapsed timebase
@@ -212,13 +216,14 @@
* @param appUsageHistory the usage record for the app being updated
* @param packageName name of the app being updated, for logging purposes
* @param newBucket the bucket to set the app to
+ * @param usageReason the sub-reason for usage, one of REASON_SUB_USAGE_*
* @param elapsedRealtime mark as used time if non-zero
* @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
* with bucket values of ACTIVE and WORKING_SET.
* @return
*/
public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
- int newBucket, long elapsedRealtime, long timeout) {
+ int newBucket, int usageReason, long elapsedRealtime, long timeout) {
// Set the timeout if applicable
if (timeout > elapsedRealtime) {
// Convert to elapsed timebase
@@ -246,10 +251,10 @@
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
.currentBucket
- + ", reason=" + appUsageHistory.bucketingReason);
+ + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
}
}
- appUsageHistory.bucketingReason = REASON_USAGE;
+ appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason;
return appUsageHistory;
}
@@ -262,16 +267,17 @@
* @param packageName
* @param userId
* @param newBucket the bucket to set the app to
- * @param elapsedRealtime mark as used time if non-zero
+ * @param usageReason sub reason for usage
+ * @param nowElapsed mark as used time if non-zero
* @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
* with bucket values of ACTIVE and WORKING_SET.
* @return
*/
public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
- long nowElapsed, long timeout) {
+ int usageReason, long nowElapsed, long timeout) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
- return reportUsage(history, packageName, newBucket, nowElapsed, timeout);
+ return reportUsage(history, packageName, newBucket, usageReason, nowElapsed, timeout);
}
private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
@@ -293,7 +299,7 @@
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
appUsageHistory.lastPredictedTime = getElapsedTime(0);
appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
- appUsageHistory.bucketingReason = REASON_DEFAULT;
+ appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
appUsageHistory.lastInformedBucket = -1;
appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago
userHistory.put(packageName, appUsageHistory);
@@ -328,18 +334,18 @@
}
public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
- int bucket, String reason) {
+ int bucket, int reason) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
appUsageHistory.currentBucket = bucket;
appUsageHistory.bucketingReason = reason;
- if (reason.startsWith(REASON_PREDICTED)) {
+ if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
}
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
- + ", reason=" + appUsageHistory.bucketingReason);
+ + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
}
}
@@ -393,11 +399,11 @@
return buckets;
}
- public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
+ public int getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, false);
- return appUsageHistory != null ? appUsageHistory.bucketingReason : null;
+ return appUsageHistory != null ? appUsageHistory.bucketingReason : 0;
}
public long getElapsedTime(long elapsedRealtime) {
@@ -411,11 +417,11 @@
elapsedRealtime, true);
if (idle) {
appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
- appUsageHistory.bucketingReason = REASON_FORCED;
+ appUsageHistory.bucketingReason = REASON_MAIN_FORCED;
} else {
appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
// This is to pretend that the app was just used, don't freeze the state anymore.
- appUsageHistory.bucketingReason = REASON_USAGE;
+ appUsageHistory.bucketingReason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
}
return appUsageHistory.currentBucket;
}
@@ -516,7 +522,7 @@
appUsageHistory.currentBucket = currentBucketString == null
? STANDBY_BUCKET_ACTIVE
: Integer.parseInt(currentBucketString);
- appUsageHistory.bucketingReason =
+ String bucketingReason =
parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
appUsageHistory.lastJobRunTime = getLongValue(parser,
ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
@@ -524,8 +530,13 @@
ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
- if (appUsageHistory.bucketingReason == null) {
- appUsageHistory.bucketingReason = REASON_DEFAULT;
+ appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
+ if (bucketingReason != null) {
+ try {
+ appUsageHistory.bucketingReason =
+ Integer.parseInt(bucketingReason, 16);
+ } catch (NumberFormatException nfe) {
+ }
}
appUsageHistory.lastInformedBucket = -1;
userHistory.put(packageName, appUsageHistory);
@@ -574,7 +585,8 @@
Long.toString(history.lastPredictedTime));
xml.attribute(null, ATTR_CURRENT_BUCKET,
Integer.toString(history.currentBucket));
- xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
+ xml.attribute(null, ATTR_BUCKETING_REASON,
+ Integer.toHexString(history.bucketingReason));
if (history.bucketActiveTimeoutTime > 0) {
xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
.bucketActiveTimeoutTime));
@@ -600,7 +612,7 @@
}
public void dump(IndentingPrintWriter idpw, int userId, String pkg) {
- idpw.println("Package idle stats:");
+ idpw.println("App Standby States:");
idpw.increaseIndent();
ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -615,24 +627,25 @@
continue;
}
idpw.print("package=" + packageName);
- idpw.print(" userId=" + userId);
- idpw.print(" lastUsedElapsed=");
+ idpw.print(" u=" + userId);
+ idpw.print(" bucket=" + appUsageHistory.currentBucket
+ + " reason="
+ + UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
+ idpw.print(" used=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
- idpw.print(" lastUsedScreenOn=");
+ idpw.print(" usedScr=");
TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
- idpw.print(" lastPredictedTime=");
+ idpw.print(" lastPred=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
- idpw.print(" bucketActiveTimeoutTime=");
- TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketActiveTimeoutTime,
+ idpw.print(" activeLeft=");
+ TimeUtils.formatDuration(appUsageHistory.bucketActiveTimeoutTime - totalElapsedTime,
idpw);
- idpw.print(" bucketWorkingSetTimeoutTime=");
- TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketWorkingSetTimeoutTime,
+ idpw.print(" wsLeft=");
+ TimeUtils.formatDuration(appUsageHistory.bucketWorkingSetTimeoutTime - totalElapsedTime,
idpw);
- idpw.print(" lastJobRunTime=");
+ idpw.print(" lastJob=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
- idpw.print(" bucket=" + appUsageHistory.currentBucket
- + " reason=" + appUsageHistory.bucketingReason);
idpw.println();
}
idpw.println();
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index f40aa5b..e836677 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -16,11 +16,20 @@
package com.android.server.usage;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -129,8 +138,8 @@
STANDBY_BUCKET_RARE
};
- // Expiration time for predicted bucket
- private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR;
+ /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
+ private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR;
/**
* Indicates the maximum wait time for admin data to be available;
@@ -187,6 +196,8 @@
long mNotificationSeenTimeoutMillis;
/** Minimum time a system update event should keep the buckets elevated. */
long mSystemUpdateUsageTimeoutMillis;
+ /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
+ long mPredictionTimeoutMillis;
volatile boolean mAppIdleEnabled;
boolean mAppIdleTempParoled;
@@ -223,24 +234,30 @@
// Whether the bucket change is because the user has started interacting with the app
boolean isUserInteraction;
- StandbyUpdateRecord(String pkgName, int userId, int bucket, boolean isInteraction) {
+ // Reason for bucket change
+ int reason;
+
+ StandbyUpdateRecord(String pkgName, int userId, int bucket, int reason,
+ boolean isInteraction) {
this.packageName = pkgName;
this.userId = userId;
this.bucket = bucket;
+ this.reason = reason;
this.isUserInteraction = isInteraction;
}
public static StandbyUpdateRecord obtain(String pkgName, int userId,
- int bucket, boolean isInteraction) {
+ int bucket, int reason, boolean isInteraction) {
synchronized (sStandbyUpdatePool) {
final int size = sStandbyUpdatePool.size();
if (size < 1) {
- return new StandbyUpdateRecord(pkgName, userId, bucket, isInteraction);
+ return new StandbyUpdateRecord(pkgName, userId, bucket, reason, isInteraction);
}
StandbyUpdateRecord r = sStandbyUpdatePool.remove(size - 1);
r.packageName = pkgName;
r.userId = userId;
r.bucket = bucket;
+ r.reason = reason;
r.isUserInteraction = isInteraction;
return r;
}
@@ -339,10 +356,11 @@
if (!packageName.equals(providerPkgName)) {
synchronized (mAppIdleLock) {
AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
- STANDBY_BUCKET_ACTIVE, elapsedRealtime,
+ STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
+ elapsedRealtime,
elapsedRealtime + mStrongUsageTimeoutMillis);
maybeInformListeners(packageName, userId, elapsedRealtime,
- appUsage.currentBucket, false);
+ appUsage.currentBucket, appUsage.bucketingReason, false);
}
}
} catch (PackageManager.NameNotFoundException e) {
@@ -497,50 +515,54 @@
if (isSpecial) {
synchronized (mAppIdleLock) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
+ STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
}
maybeInformListeners(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, false);
+ STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false);
} else {
synchronized (mAppIdleLock) {
final AppIdleHistory.AppUsageHistory app =
mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
- String reason = app.bucketingReason;
+ int reason = app.bucketingReason;
+ final int oldMainReason = reason & REASON_MAIN_MASK;
// If the bucket was forced by the user/developer, leave it alone.
// A usage event will be the only way to bring it out of this forced state
- if (REASON_FORCED.equals(app.bucketingReason)) {
+ if (oldMainReason == REASON_MAIN_FORCED) {
return;
}
final int oldBucket = app.currentBucket;
int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
boolean predictionLate = false;
// Compute age-based bucket
- if (REASON_DEFAULT.equals(app.bucketingReason)
- || REASON_USAGE.equals(app.bucketingReason)
- || REASON_TIMEOUT.equals(app.bucketingReason)
+ if (oldMainReason == REASON_MAIN_DEFAULT
+ || oldMainReason == REASON_MAIN_USAGE
+ || oldMainReason == REASON_MAIN_TIMEOUT
|| (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
newBucket = getBucketForLocked(packageName, userId,
elapsedRealtime);
if (DEBUG) {
Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
}
- reason = REASON_TIMEOUT;
+ reason = REASON_MAIN_TIMEOUT;
}
// Check if the app is within one of the timeouts for forced bucket elevation
final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
if (newBucket >= STANDBY_BUCKET_ACTIVE
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_ACTIVE;
- reason = REASON_USAGE;
+ reason = app.bucketingReason;
if (DEBUG) {
Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
}
} else if (newBucket >= STANDBY_BUCKET_WORKING_SET
&& app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_WORKING_SET;
- reason = REASON_USAGE;
+ // If it was already there, keep the reason, else assume timeout to WS
+ reason = (newBucket == oldBucket)
+ ? app.bucketingReason
+ : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
if (DEBUG) {
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
@@ -553,43 +575,41 @@
mAppIdleHistory.setAppStandbyBucket(packageName, userId,
elapsedRealtime, newBucket, reason);
maybeInformListeners(packageName, userId, elapsedRealtime,
- newBucket, false);
+ newBucket, reason, false);
}
}
}
}
+ /** Returns true if there hasn't been a prediction for the app in a while. */
private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
- return app.bucketingReason != null
- && app.bucketingReason.startsWith(REASON_PREDICTED)
+ return (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED
&& app.lastPredictedTime > 0
&& mAppIdleHistory.getElapsedTime(elapsedRealtime)
- - app.lastPredictedTime > PREDICTION_TIMEOUT;
+ - app.lastPredictedTime > mPredictionTimeoutMillis;
}
- private boolean hasBucketTimeoutPassed(AppIdleHistory.AppUsageHistory app,
- long elapsedRealtime) {
- final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
- return app.bucketActiveTimeoutTime < elapsedTimeAdjusted
- && app.bucketWorkingSetTimeoutTime < elapsedTimeAdjusted;
- }
-
+ /** Inform listeners if the bucket has changed since it was last reported to listeners */
private void maybeInformListeners(String packageName, int userId,
- long elapsedRealtime, int bucket, boolean userStartedInteracting) {
+ long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
synchronized (mAppIdleLock) {
- // TODO: fold these into one call + lookup for efficiency if needed
if (mAppIdleHistory.shouldInformListeners(packageName, userId,
elapsedRealtime, bucket)) {
- StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
- bucket, userStartedInteracting);
+ final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
+ bucket, reason, userStartedInteracting);
if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
- StandbyUpdateRecord.obtain(packageName, userId,
- bucket, userStartedInteracting)));
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
}
}
}
+ /**
+ * Evaluates next bucket based on time since last used and the bucketing thresholds.
+ * @param packageName the app
+ * @param userId the user
+ * @param elapsedRealtime as the name suggests, current elapsed time
+ * @return the bucket for the app, based on time since last used
+ */
@GuardedBy("mAppIdleLock")
@StandbyBuckets int getBucketForLocked(String packageName, int userId,
long elapsedRealtime) {
@@ -675,17 +695,20 @@
final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
event.mPackage, userId, elapsedRealtime);
final int prevBucket = appHistory.currentBucket;
- final String prevBucketReason = appHistory.bucketingReason;
+ final int prevBucketReason = appHistory.bucketingReason;
final long nextCheckTime;
+ final int subReason = usageEventToSubReason(event.mEventType);
+ final int reason = REASON_MAIN_USAGE | subReason;
if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
// Mild usage elevates to WORKING_SET but doesn't change usage time.
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_WORKING_SET,
+ STANDBY_BUCKET_WORKING_SET, subReason,
0, elapsedRealtime + mNotificationSeenTimeoutMillis);
nextCheckTime = mNotificationSeenTimeoutMillis;
+
} else {
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_ACTIVE,
+ STANDBY_BUCKET_ACTIVE, subReason,
elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
nextCheckTime = mStrongUsageTimeoutMillis;
}
@@ -695,9 +718,9 @@
final boolean userStartedInteracting =
appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
prevBucket != appHistory.currentBucket &&
- prevBucketReason != REASON_USAGE;
+ (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
maybeInformListeners(event.mPackage, userId, elapsedRealtime,
- appHistory.currentBucket, userStartedInteracting);
+ appHistory.currentBucket, reason, userStartedInteracting);
if (previouslyIdle) {
notifyBatteryStats(event.mPackage, userId, false);
@@ -706,6 +729,17 @@
}
}
+ private int usageEventToSubReason(int eventType) {
+ switch (eventType) {
+ case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+ case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+ case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
+ case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
+ case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
+ default: return 0;
+ }
+ }
+
/**
* Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
* then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
@@ -731,7 +765,8 @@
userId, elapsedRealtime);
// Inform listeners if necessary
if (previouslyIdle != stillIdle) {
- maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket, false);
+ maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
+ REASON_MAIN_FORCED, false);
if (!stillIdle) {
notifyBatteryStats(packageName, userId, idle);
}
@@ -962,11 +997,11 @@
}
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
- String reason, long elapsedRealtime) {
+ int reason, long elapsedRealtime) {
synchronized (mAppIdleLock) {
AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
- boolean predicted = reason != null && reason.startsWith(REASON_PREDICTED);
+ boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
// Don't allow changing bucket if higher than ACTIVE
if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
@@ -979,7 +1014,7 @@
}
// If the bucket was forced, don't allow prediction to override
- if (app.bucketingReason.equals(REASON_FORCED) && predicted) return;
+ if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED && predicted) return;
// If the bucket is required to stay in a higher state for a specified duration, don't
// override unless the duration has passed
@@ -989,14 +1024,18 @@
if (newBucket > STANDBY_BUCKET_ACTIVE
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_ACTIVE;
- reason = REASON_USAGE;
+ reason = app.bucketingReason;
if (DEBUG) {
Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
}
} else if (newBucket > STANDBY_BUCKET_WORKING_SET
&& app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_WORKING_SET;
- reason = REASON_USAGE;
+ if (app.currentBucket != newBucket) {
+ reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+ } else {
+ reason = app.bucketingReason;
+ }
if (DEBUG) {
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
@@ -1006,7 +1045,7 @@
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
reason);
}
- maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, false);
+ maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
}
@VisibleForTesting
@@ -1106,11 +1145,12 @@
return packageName != null && packageName.equals(activeScorer);
}
- void informListeners(String packageName, int userId, int bucket, boolean userInteraction) {
+ void informListeners(String packageName, int userId, int bucket, int reason,
+ boolean userInteraction) {
final boolean idle = bucket >= STANDBY_BUCKET_RARE;
synchronized (mPackageAccessListeners) {
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
+ listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
if (userInteraction) {
listener.onUserInteractionStarted(packageName, userId);
}
@@ -1188,7 +1228,8 @@
if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
// Mark app as used for 2 hours. After that it can timeout to whatever the
// past usage pattern was.
- mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, 0,
+ mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
+ REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
}
}
@@ -1369,7 +1410,8 @@
switch (msg.what) {
case MSG_INFORM_LISTENERS:
StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
- informListeners(r.packageName, r.userId, r.bucket, r.isUserInteraction);
+ informListeners(r.packageName, r.userId, r.bucket, r.reason,
+ r.isUserInteraction);
r.recycle();
break;
@@ -1477,7 +1519,7 @@
"notification_seen_duration";
private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
"system_update_usage_duration";
-
+ private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -1547,6 +1589,9 @@
mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis
(KEY_SYSTEM_UPDATE_HOLD_DURATION,
COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR);
+ mPredictionTimeoutMillis = mParser.getDurationMillis
+ (KEY_PREDICTION_TIMEOUT,
+ COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT);
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index dedf967..43ac58a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -120,10 +120,10 @@
new UsageStatsManagerInternal.AppIdleStateChangeListener() {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
- int bucket) {
+ int bucket, int reason) {
Event event = new Event();
event.mEventType = Event.STANDBY_BUCKET_CHANGED;
- event.mBucket = bucket;
+ event.mBucketAndReason = (bucket << 16) | (reason & 0xFFFF);
event.mPackage = packageName;
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
@@ -741,9 +741,9 @@
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
- final String reason = shellCaller
- ? UsageStatsManager.REASON_FORCED
- : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
+ final int reason = shellCaller
+ ? UsageStatsManager.REASON_MAIN_FORCED
+ : UsageStatsManager.REASON_MAIN_PREDICTED;
final long token = Binder.clearCallingIdentity();
try {
// Caller cannot set their own standby state
@@ -798,9 +798,9 @@
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
- final String reason = shellCaller
- ? UsageStatsManager.REASON_FORCED
- : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
+ final int reason = shellCaller
+ ? UsageStatsManager.REASON_MAIN_FORCED
+ : UsageStatsManager.REASON_MAIN_PREDICTED;
final long token = Binder.clearCallingIdentity();
try {
final long elapsedRealtime = SystemClock.elapsedRealtime();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index d1ed599..5e7e80d 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,10 +26,7 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
-import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.Log;
-import android.util.LogWriter;
import java.io.IOException;
import java.net.ProtocolException;
@@ -175,7 +172,7 @@
event.mShortcutId = (id != null) ? id.intern() : null;
break;
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
- event.mBucket = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
+ event.mBucketAndReason = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
break;
}
@@ -281,8 +278,8 @@
}
break;
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
- if (event.mBucket != 0) {
- XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucket);
+ if (event.mBucketAndReason != 0) {
+ XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucketAndReason);
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 8afc511..3fbcd81 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -520,7 +520,8 @@
pw.printPair("shortcutId", event.mShortcutId);
}
if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
- pw.printPair("standbyBucket", event.mBucket);
+ pw.printPair("standbyBucket", event.getStandbyBucket());
+ pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
}
pw.printHexPair("flags", event.mFlags);
pw.println();
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index b6be3c5..82a7450 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1215,6 +1216,7 @@
}
/** @hide */
+ @TestApi
public void setSystemAndNetworkId(int systemId, int networkId) {
this.mSystemId = systemId;
this.mNetworkId = networkId;