Merge "Few improvements on Augmented Autofill."
diff --git a/api/system-current.txt b/api/system-current.txt
index f718c2a..6a71c04 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3892,14 +3892,6 @@
}
-package android.net.wifi.p2p {
-
- public class WifiP2pManager {
- method public void factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
- }
-
-}
-
package android.net.wifi.rtt {
public static final class RangingRequest.Builder {
@@ -5789,6 +5781,8 @@
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
+ method public void setDefaultDataSubId(int);
+ method public void setDefaultSmsSubId(int);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 32fc0dc..9dcd122 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -119,6 +119,7 @@
/**
* @deprecated Use {@link #addConfig(long, byte[])}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public boolean addConfiguration(long configKey, byte[] config) {
try {
@@ -154,6 +155,7 @@
/**
* @deprecated Use {@link #removeConfig(long)}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public boolean removeConfiguration(long configKey) {
try {
@@ -222,6 +224,7 @@
/**
* @deprecated Use {@link #setBroadcastSubscriber(PendingIntent, long, long)}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public boolean setBroadcastSubscriber(
long configKey, long subscriberId, PendingIntent pendingIntent) {
@@ -275,6 +278,7 @@
/**
* @deprecated Use {@link #setFetchReportsOperation(PendingIntent, long)}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
try {
@@ -312,6 +316,7 @@
/**
* @deprecated Use {@link #getReports(long)}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public @Nullable byte[] getData(long configKey) {
try {
@@ -348,6 +353,7 @@
/**
* @deprecated Use {@link #getStatsMetadata()}
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public @Nullable byte[] getMetadata() {
try {
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 78b7d48..257122d 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -75,6 +75,9 @@
/** The current windowing mode of the configuration. */
private @WindowingMode int mWindowingMode;
+ /** The display windowing mode of the configuration */
+ private @WindowingMode int mDisplayWindowingMode;
+
/** Windowing mode is currently not defined. */
public static final int WINDOWING_MODE_UNDEFINED = 0;
/** Occupies the full area of the screen or the parent container. */
@@ -176,6 +179,10 @@
/** Bit that indicates that the {@link #mRotation} changed.
* @hide */
public static final int WINDOW_CONFIG_ROTATION = 1 << 5;
+ /** Bit that indicates that the {@link #mDisplayWindowingMode} changed.
+ * @hide */
+ public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 6;
+
/** @hide */
@IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
WINDOW_CONFIG_BOUNDS,
@@ -184,6 +191,7 @@
WINDOW_CONFIG_ACTIVITY_TYPE,
WINDOW_CONFIG_ALWAYS_ON_TOP,
WINDOW_CONFIG_ROTATION,
+ WINDOW_CONFIG_DISPLAY_WINDOWING_MODE,
})
public @interface WindowConfig {}
@@ -211,6 +219,7 @@
dest.writeInt(mActivityType);
dest.writeInt(mAlwaysOnTop);
dest.writeInt(mRotation);
+ dest.writeInt(mDisplayWindowingMode);
}
private void readFromParcel(Parcel source) {
@@ -220,6 +229,7 @@
mActivityType = source.readInt();
mAlwaysOnTop = source.readInt();
mRotation = source.readInt();
+ mDisplayWindowingMode = source.readInt();
}
@Override
@@ -322,6 +332,12 @@
return mWindowingMode;
}
+ /** @hide */
+ public void setDisplayWindowingMode(@WindowingMode int windowingMode) {
+ mDisplayWindowingMode = windowingMode;
+ }
+
+
public void setActivityType(@ActivityType int activityType) {
if (mActivityType == activityType) {
return;
@@ -351,6 +367,7 @@
setActivityType(other.mActivityType);
setAlwaysOnTop(other.mAlwaysOnTop);
setRotation(other.mRotation);
+ setDisplayWindowingMode(other.mDisplayWindowingMode);
}
/** Set this object to completely undefined.
@@ -367,6 +384,7 @@
setActivityType(ACTIVITY_TYPE_UNDEFINED);
setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
setRotation(ROTATION_UNDEFINED);
+ setDisplayWindowingMode(WINDOWING_MODE_UNDEFINED);
}
/**
@@ -407,6 +425,11 @@
changed |= WINDOW_CONFIG_ROTATION;
setRotation(delta.mRotation);
}
+ if (delta.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED
+ && mDisplayWindowingMode != delta.mDisplayWindowingMode) {
+ changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
+ setDisplayWindowingMode(delta.mDisplayWindowingMode);
+ }
return changed;
}
@@ -455,6 +478,11 @@
changes |= WINDOW_CONFIG_ROTATION;
}
+ if ((compareUndefined || other.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED)
+ && mDisplayWindowingMode != other.mDisplayWindowingMode) {
+ changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
+ }
+
return changes;
}
@@ -493,6 +521,8 @@
if (n != 0) return n;
n = mRotation - that.mRotation;
if (n != 0) return n;
+ n = mDisplayWindowingMode - that.mDisplayWindowingMode;
+ if (n != 0) return n;
// if (n != 0) return n;
return n;
@@ -522,6 +552,7 @@
result = 31 * result + mActivityType;
result = 31 * result + mAlwaysOnTop;
result = 31 * result + mRotation;
+ result = 31 * result + mDisplayWindowingMode;
return result;
}
@@ -531,6 +562,7 @@
return "{ mBounds=" + mBounds
+ " mAppBounds=" + mAppBounds
+ " mWindowingMode=" + windowingModeToString(mWindowingMode)
+ + " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode)
+ " mActivityType=" + activityTypeToString(mActivityType)
+ " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
+ " mRotation=" + (mRotation == ROTATION_UNDEFINED
@@ -603,7 +635,8 @@
* @hide
*/
public boolean hasWindowDecorCaption() {
- return mWindowingMode == WINDOWING_MODE_FREEFORM;
+ return mActivityType == ACTIVITY_TYPE_STANDARD && (mWindowingMode == WINDOWING_MODE_FREEFORM
+ || mDisplayWindowingMode == WINDOWING_MODE_FREEFORM);
}
/**
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index e032c18..37c84bd 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -152,6 +152,17 @@
"android:query-arg-last-modified-after";
/**
+ * Key for {@link DocumentsProvider} to decide whether the files that
+ * have been added to MediaStore should be excluded. If the value is
+ * true, exclude them. Otherwise, include them.
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ * {@hide}
+ */
+ public static final String QUERY_ARG_EXCLUDE_MEDIA = "android:query-arg-exclude-media";
+
+ /**
* Sets the desired initial location visible to user when file chooser is shown.
*
* <p>Applicable to {@link Intent} with actions:
@@ -1017,6 +1028,7 @@
/**
* Get the handled query arguments from the query bundle. The handled arguments are
+ * {@link DocumentsContract#QUERY_ARG_EXCLUDE_MEDIA},
* {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
* {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
* {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER} and
@@ -1032,6 +1044,11 @@
}
final ArrayList<String> args = new ArrayList<>();
+
+ if (queryArgs.keySet().contains(QUERY_ARG_EXCLUDE_MEDIA)) {
+ args.add(QUERY_ARG_EXCLUDE_MEDIA);
+ }
+
if (queryArgs.keySet().contains(QUERY_ARG_DISPLAY_NAME)) {
args.add(QUERY_ARG_DISPLAY_NAME);
}
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 58f8213..6ab72c7 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -677,6 +677,7 @@
* cursor. If {@code null} all supported columns should be
* included.
* @param queryArgs the query arguments.
+ * {@link DocumentsContract#QUERY_ARG_EXCLUDE_MEDIA},
* {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
* {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
* {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER},
diff --git a/core/res/res/layout/notification_material_reply_text.xml b/core/res/res/layout/notification_material_reply_text.xml
index 71632a2..2b15dce 100644
--- a/core/res/res/layout/notification_material_reply_text.xml
+++ b/core/res/res/layout/notification_material_reply_text.xml
@@ -39,7 +39,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:visibility="gone"
- android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
<TextView
@@ -48,7 +48,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:visibility="gone"
- android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
<LinearLayout
@@ -64,7 +64,7 @@
android:layout_weight="1"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:layout_gravity="center"
- android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
<ProgressBar
android:id="@+id/notification_material_reply_progress"
diff --git a/core/res/res/layout/notification_template_ambient_header.xml b/core/res/res/layout/notification_template_ambient_header.xml
index c00acd5..be5d9b4 100644
--- a/core/res/res/layout/notification_template_ambient_header.xml
+++ b/core/res/res/layout/notification_template_ambient_header.xml
@@ -24,5 +24,5 @@
android:layout_height="wrap_content">
<include
layout="@layout/notification_template_header"
- android:theme="@style/Theme.Material.Notification.Ambient"/>
+ android:theme="@style/Theme.DeviceDefault.Notification.Ambient"/>
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 4bf1ad6..5ba1cf2 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -17,7 +17,7 @@
<!-- extends ViewGroup -->
<NotificationHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/Theme.Material.Notification"
+ android:theme="@style/Theme.DeviceDefault.Notification"
android:id="@+id/notification_header"
android:orientation="horizontal"
android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index c8864c2..2c6064e 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -24,7 +24,7 @@
android:paddingEnd="@dimen/notification_extra_margin_ambient"
>
<include layout="@layout/notification_template_ambient_header"
- android:theme="@style/Theme.Material.Notification.Ambient" />
+ android:theme="@style/Theme.DeviceDefault.Notification.Ambient" />
<LinearLayout
android:id="@+id/notification_action_list_margin_target"
@@ -51,8 +51,7 @@
android:orientation="vertical"
>
<TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.Material.Notification.Title"
- android:fontFamily="sans-serif"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_horizontal"
@@ -65,7 +64,7 @@
<TextView android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification"
android:singleLine="false"
android:layout_weight="1"
android:gravity="top|center_horizontal"
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 26eb4e7..d5ea96f 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -53,7 +53,7 @@
android:layout_marginTop="@dimen/notification_progress_margin_top"
android:layout_marginBottom="6dp"/>
<com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_text_margin_top"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index d6f0e1d..eb89258 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -53,7 +53,7 @@
android:layout_marginTop="@dimen/notification_progress_margin_top"
android:layout_marginBottom="2dp"/>
<TextView android:id="@+id/inbox_text0"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -62,7 +62,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text1"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -71,7 +71,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text2"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -80,7 +80,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text3"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -89,7 +89,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text4"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -98,7 +98,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text5"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -107,7 +107,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text6"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 0717d96..483b479 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -34,7 +34,7 @@
android:orientation="vertical">
<com.android.internal.widget.ImageFloatingTextView
android:id="@+id/message_name"
- style="@style/Widget.Material.Notification.MessagingName"
+ style="@style/Widget.DeviceDefault.Notification.MessagingName"
android:layout_width="wrap_content"
android:textAlignment="viewStart"
/>
diff --git a/core/res/res/layout/notification_template_messaging_text_message.xml b/core/res/res/layout/notification_template_messaging_text_message.xml
index 3611186..26c081e 100644
--- a/core/res/res/layout/notification_template_messaging_text_message.xml
+++ b/core/res/res/layout/notification_template_messaging_text_message.xml
@@ -18,5 +18,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/message_text"
android:textAlignment="viewStart"
- style="@style/Widget.Material.Notification.MessagingText"
+ style="@style/Widget.DeviceDefault.Notification.MessagingText"
/>
diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml
index 6459bb8..622f080 100644
--- a/core/res/res/layout/notification_template_part_line1.xml
+++ b/core/res/res/layout/notification_template_part_line1.xml
@@ -22,7 +22,7 @@
android:orientation="horizontal"
>
<TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.Material.Notification.Title"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
@@ -31,7 +31,7 @@
android:textAlignment="viewStart"
/>
<TextView android:id="@+id/text_line_1"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end|bottom"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 3b27666..01b14ae 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
<com.android.internal.widget.ImageFloatingTextView xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/Widget.Material.Notification.Text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index d722961..c03d570 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -113,6 +113,15 @@
<style name="Widget.DeviceDefault.ListView.White" parent="Widget.Material.ListView.White"/>
<style name="Widget.DeviceDefault.MediaRouteButton" parent="Widget.Material.MediaRouteButton" />
<style name="Widget.DeviceDefault.NumberPicker" parent="Widget.Material.NumberPicker"/>
+ <style name="Widget.DeviceDefault.Notification.Text" parent="Widget.Material.Notification.Text">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
+ </style>
+ <style name="Widget.DeviceDefault.Notification.MessagingText" parent="Widget.Material.Notification.MessagingText">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
+ </style>
+ <style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item>
+ </style>
<style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/>
<style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/>
<style name="Widget.DeviceDefault.ProgressBar.Large.Inverse" parent="Widget.Material.ProgressBar.Large.Inverse"/>
@@ -250,6 +259,16 @@
<style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title" parent="TextAppearance.Material.Widget.ActionBar.Title">
<item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
</style>
+
+ <!-- Notification Styles -->
+ <style name="TextAppearance.DeviceDefault.Notification" parent="TextAppearance.Material.Notification">
+ <item name="fontFamily">@string/config_headlineFontFamily</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title">
+ <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply" />
+
<style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle" parent="TextAppearance.Material.Widget.ActionBar.Subtitle"/>
<style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title" parent="TextAppearance.Material.Widget.ActionMode.Title"/>
<style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle" parent="TextAppearance.Material.Widget.ActionMode.Subtitle"/>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 67b3c92..5a7199d 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -1313,4 +1313,5 @@
<item name="gravity">top|center_horizontal</item>
</style>
+
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index fa009bd..fec101a 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1724,4 +1724,10 @@
<item name="layout_gravity">center</item>
</style>
+ <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
+ </style>
+
+ <style name="Theme.DeviceDefault.Notification.Ambient" parent="@style/Theme.Material.Notification.Ambient">
+ </style>
+
</resources>
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index a10b212..4b99a13 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -3501,7 +3501,7 @@
}
}
- private native void native_releaseDrm();
+ private native void native_releaseDrm(long mSrcId);
/**
* A key request/response exchange occurs between the app and a license server
@@ -3821,7 +3821,8 @@
}
}
- private native void native_prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
+ private native void native_prepareDrm(
+ long srcId, @NonNull byte[] uuid, @NonNull byte[] drmSessionId);
// Instantiated from the native side
@SuppressWarnings("unused")
@@ -4064,6 +4065,7 @@
static final int PROVISION_TIMEOUT_MS = 60000;
final DataSourceDesc mDSD;
+ final long mSrcId;
//--- guarded by |this| start
MediaDrm mDrmObj;
@@ -4075,8 +4077,9 @@
Future<?> mProvisionResult;
//--- guarded by |this| end
- DrmHandle(DataSourceDesc dsd) {
+ DrmHandle(DataSourceDesc dsd, long srcId) {
mDSD = dsd;
+ mSrcId = srcId;
}
void prepare(UUID uuid) throws UnsupportedSchemeException,
@@ -4186,7 +4189,8 @@
// Sending it down to native/mediaserver to create the crypto object
// This call could simply fail due to bad player state, e.g., after play().
- MediaPlayer2.this.native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
+ final MediaPlayer2 mp2 = MediaPlayer2.this;
+ mp2.native_prepareDrm(mSrcId, getByteArrayFromUUID(uuid), mDrmSessionId);
Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
} catch (Exception e) { //ResourceBusyException, NotProvisionedException
@@ -4367,7 +4371,7 @@
// exception if we're in a non-stopped/prepared state.
// for cleaning native/mediaserver crypto object
- native_releaseDrm();
+ native_releaseDrm(mSrcId);
// for cleaning client-side MediaDrm object; only called if above has succeeded
cleanDrmObj();
@@ -4573,7 +4577,7 @@
SourceInfo(DataSourceDesc dsd) {
this.mDSD = dsd;
- mDrmHandle = new DrmHandle(dsd);
+ mDrmHandle = new DrmHandle(dsd, mId);
}
void close() {
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 4567492..8b6009e 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1192,7 +1192,7 @@
}
static void android_media_MediaPlayer2_prepareDrm(JNIEnv *env, jobject thiz,
- jbyteArray uuidObj, jbyteArray drmSessionIdObj)
+ jlong srcId, jbyteArray uuidObj, jbyteArray drmSessionIdObj)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
@@ -1225,7 +1225,7 @@
return;
}
- status_t err = mp->prepareDrm(uuid.array(), drmSessionId);
+ status_t err = mp->prepareDrm(srcId, uuid.array(), drmSessionId);
if (err != OK) {
if (err == INVALID_OPERATION) {
jniThrowException(
@@ -1243,7 +1243,7 @@
}
}
-static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz)
+static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz, jlong srcId)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -1251,7 +1251,7 @@
return;
}
- status_t err = mp->releaseDrm();
+ status_t err = mp->releaseDrm(srcId);
if (err != OK) {
if (err == INVALID_OPERATION) {
jniThrowException(
@@ -1425,8 +1425,8 @@
{"native_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
{"native_attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect},
// Modular DRM
- { "native_prepareDrm", "([B[B)V", (void *)android_media_MediaPlayer2_prepareDrm },
- { "native_releaseDrm", "()V", (void *)android_media_MediaPlayer2_releaseDrm },
+ { "native_prepareDrm", "(J[B[B)V", (void *)android_media_MediaPlayer2_prepareDrm },
+ { "native_releaseDrm", "(J)V", (void *)android_media_MediaPlayer2_releaseDrm },
// AudioRouting
{"native_setPreferredDevice", "(Landroid/media/AudioDeviceInfo;)Z", (void *)android_media_MediaPlayer2_setPreferredDevice},
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
index 12699d5..18dc185 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
@@ -58,4 +58,8 @@
encoder.endStream();
return true;
}
+
+ public int getDisplayId() {
+ return mWrapped.getDisplayId();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index a90a7d2..ba89fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -140,7 +140,7 @@
createDialogs();
if (!mDialogs.isEmpty()) {
- getComponent(CommandQueue.class).addCallbacks(this);
+ getComponent(CommandQueue.class).addCallback(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index aa08562..e8ef454 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -45,7 +45,7 @@
.withCallback(this::onExtensionCallback)
.build();
mPlugin = mExtension.get();
- SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
}
private void onExtensionCallback(GlobalActions newPlugin) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 0394998..dc11b4c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -55,12 +55,12 @@
mContext = context;
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallback(this);
}
@Override
public void destroy() {
- SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
+ SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
if (mGlobalActions != null) {
mGlobalActions.destroy();
mGlobalActions = null;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 7792e17..37c8163 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -58,7 +58,7 @@
: com.android.systemui.pip.phone.PipManager.getInstance();
mPipManager.initialize(mContext);
- getComponent(CommandQueue.class).addCallbacks(this);
+ getComponent(CommandQueue.class).addCallback(this);
putComponent(PipUI.class, this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 953eb70..2acbea4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -108,12 +108,12 @@
mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
}
}
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
}
@Override
public void onDestroyView() {
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallback(this);
super.onDestroyView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index d1c2df5..ca8e824 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -308,7 +308,7 @@
protected static List<String> loadTileSpecs(Context context, String tileList) {
final Resources res = context.getResources();
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
- if (tileList == null) {
+ if (TextUtils.isEmpty(tileList)) {
tileList = res.getString(R.string.quick_settings_tiles);
if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 0702d74..f13b565 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -37,7 +37,7 @@
@Override
public void start() {
- getComponent(CommandQueue.class).addCallbacks(this);
+ getComponent(CommandQueue.class).addCallback(this);
putComponent(Recents.class, this);
mImpl = createRecentsImplementationFromConfig();
mImpl.onStart(mContext, this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index b550274..95019ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -35,6 +35,8 @@
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.policy.CallbackController;
import java.util.ArrayList;
@@ -45,7 +47,7 @@
* coalescing these calls so they don't stack up. For the calls
* are coalesced, note that they are all idempotent.
*/
-public class CommandQueue extends IStatusBar.Stub {
+public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks> {
private static final int INDEX_MASK = 0xffff;
private static final int MSG_SHIFT = 16;
private static final int MSG_MASK = 0xffff << MSG_SHIFT;
@@ -183,12 +185,12 @@
&& !ONLY_CORE_APPS;
}
- public void addCallbacks(Callbacks callbacks) {
+ public void addCallback(Callbacks callbacks) {
mCallbacks.add(callbacks);
callbacks.disable(mDisable1, mDisable2, false /* animate */);
}
- public void removeCallbacks(Callbacks callbacks) {
+ public void removeCallback(Callbacks callbacks) {
mCallbacks.remove(callbacks);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 8418cba..24570ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -120,14 +120,14 @@
@Override
public void onResume() {
super.onResume();
- mCommandQueue.addCallbacks(this);
+ mCommandQueue.addCallback(this);
mStatusBarStateController.addCallback(this);
}
@Override
public void onPause() {
super.onPause();
- mCommandQueue.removeCallbacks(this);
+ mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 1c6d292..57cc7d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -73,14 +73,14 @@
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
SysUiServiceProvider.getComponent(context, CommandQueue.class)
- .addCallbacks(this);
+ .addCallback(this);
mStatusBarStateController.addCallback(this);
mDozeAmount = mStatusBarStateController.getDozeAmount();
}
public void destroy(Context context) {
SysUiServiceProvider.getComponent(context, CommandQueue.class)
- .removeCallbacks(this);
+ .removeCallback(this);
mStatusBarStateController.removeCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 8657003..d2bfd12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -199,7 +199,7 @@
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
- mCommandQueue.addCallbacks(this);
+ mCommandQueue.addCallback(this);
mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
@@ -225,7 +225,7 @@
@Override
public void onDestroy() {
super.onDestroy();
- mCommandQueue.removeCallbacks(this);
+ mCommandQueue.removeCallback(this);
Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
mAccessibilityListener);
mContentResolver.unregisterContentObserver(mMagnificationObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index c84f3db..ee1eb42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -268,7 +268,7 @@
mLocationController.addCallback(this);
mPrivacyItemController.setListening(true);
- SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
// Clear out all old notifications on startup (only present in the case where sysui dies)
@@ -296,7 +296,7 @@
mKeyguardMonitor.removeCallback(this);
mLocationController.removeCallback(this);
mPrivacyItemController.setListening(false);
- SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
+ SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
mContext.unregisterReceiver(mIntentReceiver);
NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
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 58b9de4..962e214 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -579,6 +579,7 @@
private boolean mVibrateOnOpening;
private VibratorHelper mVibratorHelper;
protected NotificationPresenter mPresenter;
+ private boolean mPulsing;
@Override
public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -663,7 +664,7 @@
// Connect in to the status bar manager service
mCommandQueue = getComponent(CommandQueue.class);
- mCommandQueue.addCallbacks(this);
+ mCommandQueue.addCallback(this);
int[] switches = new int[9];
ArrayList<IBinder> binders = new ArrayList<>();
@@ -1544,7 +1545,7 @@
}
public boolean isPulsing() {
- return mAmbientPulseManager.hasNotifications();
+ return mPulsing;
}
public boolean isLaunchTransitionFadingAway() {
@@ -3935,6 +3936,10 @@
return;
}
+ // Set the state to pulsing, so ScrimController will know what to do once we ask it to
+ // execute the transition. The pulse callback will then be invoked when the scrims
+ // are black, indicating that StatusBar is ready to present the rest of the UI.
+ mPulsing = true;
mDozeScrimController.pulse(new PulseCallback() {
@Override
public void onPulseStarted() {
@@ -3948,6 +3953,7 @@
@Override
public void onPulseFinished() {
+ mPulsing = false;
callback.onPulseFinished();
setPulsing(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 7c17c01..4f25349 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -76,7 +76,7 @@
loadDimens();
SysUiServiceProvider.getComponent(context, CommandQueue.class)
- .addCallbacks(this);
+ .addCallback(this);
Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 3f1e826..e3f6bd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -73,7 +73,7 @@
mStatusBarStateController.addCallback(mStateListener);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mCommandQueue = getComponent(context, CommandQueue.class);
- mCommandQueue.addCallbacks(this);
+ mCommandQueue.addCallback(this);
}
private void setStatusBarState(int state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index c2af95e..aafdcd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -187,7 +187,7 @@
null, Dependency.get(Dependency.TIME_TICK_HANDLER));
Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
StatusBarIconController.ICON_BLACKLIST);
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
if (mShowDark) {
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
}
@@ -215,7 +215,7 @@
mAttached = false;
Dependency.get(TunerService.class).removeTunable(this);
SysUiServiceProvider.getComponent(getContext(), CommandQueue.class)
- .removeCallbacks(this);
+ .removeCallback(this);
if (mShowDark) {
Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index cd379c5..4a69cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -42,7 +42,7 @@
public void start() {
putComponent(TvStatusBar.class, this);
CommandQueue commandQueue = getComponent(CommandQueue.class);
- commandQueue.addCallbacks(this);
+ commandQueue.addCallback(this);
int[] switches = new int[9];
ArrayList<IBinder> binders = new ArrayList<>();
ArrayList<String> iconSlots = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 6812410..490cdd5c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -57,13 +57,13 @@
public void onViewAttachedToWindow(View v) {
mView = v;
SysUiServiceProvider.getComponent(v.getContext(), CommandQueue.class)
- .addCallbacks(this);
+ .addCallback(this);
}
@Override
public void onViewDetachedFromWindow(View v) {
SysUiServiceProvider.getComponent(mView.getContext(), CommandQueue.class)
- .removeCallbacks(this);
+ .removeCallback(this);
mView = null;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
new file mode 100644
index 0000000..78700b8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.systemui.qs;
+
+
+import static junit.framework.TestCase.assertFalse;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class QSTileHostTest extends SysuiTestCase {
+
+ @Test
+ public void testLoadTileSpecs_emptySetting() {
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
+ assertFalse(tiles.isEmpty());
+ }
+
+ @Test
+ public void testLoadTileSpecs_nullSetting() {
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
+ assertFalse(tiles.isEmpty());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index a4fe52d..a04e57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -44,7 +44,7 @@
public void setup() {
mCommandQueue = new CommandQueue();
mCallbacks = mock(Callbacks.class);
- mCommandQueue.addCallbacks(mCallbacks);
+ mCommandQueue.addCallback(mCallbacks);
verify(mCallbacks).disable(eq(0), eq(0), eq(false));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e9e8eb7..c207fef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -72,6 +72,8 @@
import com.android.systemui.assist.AssistManager;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeLog;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -123,6 +125,7 @@
@Mock private IStatusBarService mBarService;
@Mock private IDreamManager mDreamManager;
@Mock private ScrimController mScrimController;
+ @Mock private DozeScrimController mDozeScrimController;
@Mock private ArrayList<Entry> mNotificationList;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
@@ -211,7 +214,7 @@
mKeyguardViewMediator, mRemoteInputManager, mock(NotificationGroupManager.class),
mock(NotificationGroupAlertTransferHelper.class), mock(FalsingManager.class),
mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class),
- mock(DozeScrimController.class), mock(NotificationShelf.class),
+ mDozeScrimController, mock(NotificationShelf.class),
mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
mock(BubbleController.class));
mStatusBar.mContext = mContext;
@@ -570,7 +573,28 @@
}
@Test
+ public void testPulseWhileDozing_updatesScrimController() {
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mStatusBar.showKeyguardImpl();
+ // Keep track of callback to be able to stop the pulse
+ DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
+ doAnswer(invocation -> {
+ pulseCallback[0] = invocation.getArgument(0);
+ return null;
+ }).when(mDozeScrimController).pulse(any(), anyInt());
+
+ // Starting a pulse should change the scrim controller to the pulsing state
+ mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
+ DozeLog.PULSE_REASON_NOTIFICATION);
+ verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any());
+
+ // Ending a pulse should take it back to keyguard state
+ pulseCallback[0].onPulseFinished();
+ verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
+ }
+
+ @Test
public void testSetState_changesIsFullScreenUserSwitcherState() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
assertFalse(mStatusBar.isFullScreenUserSwitcherState());
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
new file mode 100644
index 0000000..f356b4f
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
@@ -0,0 +1,117 @@
+/*
+ * 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.server.backup.encryption.keys;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.Slog;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Wraps a {@link RecoveryController}'s {@link SecretKey}. These are kept in "AndroidKeyStore" (a
+ * provider for {@link java.security.KeyStore} and {@link javax.crypto.KeyGenerator}. They are also
+ * synced with the recoverable key store, wrapped by the primary key. This allows them to be
+ * recovered on a user's subsequent device through providing their lock screen secret.
+ */
+public class RecoverableKeyStoreSecondaryKey {
+ private static final String TAG = "RecoverableKeyStoreSecondaryKey";
+
+ private final String mAlias;
+ private final SecretKey mSecretKey;
+
+ /**
+ * A new instance.
+ *
+ * @param alias The alias. It is keyed with this in AndroidKeyStore and the recoverable key
+ * store.
+ * @param secretKey The key.
+ */
+ public RecoverableKeyStoreSecondaryKey(String alias, SecretKey secretKey) {
+ mAlias = checkNotNull(alias);
+ mSecretKey = checkNotNull(secretKey);
+ }
+
+ /**
+ * The ID, as stored in the recoverable {@link java.security.KeyStore}, and as used to identify
+ * wrapped tertiary keys on the backup server.
+ */
+ public String getAlias() {
+ return mAlias;
+ }
+
+ /** The secret key, to be used to wrap tertiary keys. */
+ public SecretKey getSecretKey() {
+ return mSecretKey;
+ }
+
+ /**
+ * The status of the key. i.e., whether it's been synced to remote trusted hardware.
+ *
+ * @param context The application context.
+ * @return One of {@link Status#SYNCED}, {@link Status#NOT_SYNCED} or {@link Status#DESTROYED}.
+ */
+ public @Status int getStatus(Context context) {
+ try {
+ return getStatusInternal(context);
+ } catch (InternalRecoveryServiceException e) {
+ Slog.wtf(TAG, "Internal error getting recovery status", e);
+ // Return NOT_SYNCED by default, as we do not want the backups to fail or to repeatedly
+ // attempt to reinitialize.
+ return Status.NOT_SYNCED;
+ }
+ }
+
+ private @Status int getStatusInternal(Context context) throws InternalRecoveryServiceException {
+ int status = RecoveryController.getInstance(context).getRecoveryStatus(mAlias);
+ switch (status) {
+ case RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE:
+ return Status.DESTROYED;
+ case RecoveryController.RECOVERY_STATUS_SYNCED:
+ return Status.SYNCED;
+ case RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS:
+ return Status.NOT_SYNCED;
+ default:
+ // Throw an exception if we encounter a status that doesn't match any of the above.
+ throw new InternalRecoveryServiceException(
+ "Unexpected status from getRecoveryStatus: " + status);
+ }
+ }
+
+ /** Status of a key in the recoverable key store. */
+ @IntDef({Status.NOT_SYNCED, Status.SYNCED, Status.DESTROYED})
+ public @interface Status {
+ /**
+ * The key has not yet been synced to remote trusted hardware. This may be because the user
+ * has not yet unlocked their device.
+ */
+ int NOT_SYNCED = 1;
+
+ /**
+ * The key has been synced with remote trusted hardware. It should now be recoverable on
+ * another device.
+ */
+ int SYNCED = 2;
+
+ /** The key has been lost forever. This can occur if the user disables their lock screen. */
+ int DESTROYED = 3;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
new file mode 100644
index 0000000..db5fe77
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
@@ -0,0 +1,119 @@
+/*
+ * 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.server.backup.encryption.keys;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.ByteStringUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.util.Optional;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Manages generating, deleting, and retrieving secondary keys through {@link RecoveryController}.
+ *
+ * <p>The recoverable key store will be synced remotely via the {@link RecoveryController}, allowing
+ * recovery of keys on other devices owned by the user.
+ */
+public class RecoverableKeyStoreSecondaryKeyManager {
+ private static final String BACKUP_KEY_ALIAS_PREFIX =
+ "com.android.server.backup/recoverablekeystore/";
+ private static final int BACKUP_KEY_SUFFIX_LENGTH_BITS = 128;
+ private static final int BITS_PER_BYTE = 8;
+
+ /** A new instance. */
+ public static RecoverableKeyStoreSecondaryKeyManager getInstance(Context context) {
+ return new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(context), new SecureRandom());
+ }
+
+ private final RecoveryController mRecoveryController;
+ private final SecureRandom mSecureRandom;
+
+ @VisibleForTesting
+ public RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController recoveryController, SecureRandom secureRandom) {
+ mRecoveryController = recoveryController;
+ mSecureRandom = secureRandom;
+ }
+
+ /**
+ * Generates a new recoverable key using the {@link RecoveryController}.
+ *
+ * @throws InternalRecoveryServiceException if an unexpected error occurred generating the key.
+ * @throws LockScreenRequiredException if the user does not have a lock screen. A lock screen is
+ * required to generate a recoverable key.
+ */
+ public RecoverableKeyStoreSecondaryKey generate()
+ throws InternalRecoveryServiceException, LockScreenRequiredException,
+ UnrecoverableKeyException {
+ String alias = generateId();
+ mRecoveryController.generateKey(alias);
+ SecretKey key = (SecretKey) mRecoveryController.getKey(alias);
+ if (key == null) {
+ throw new InternalRecoveryServiceException(
+ String.format(
+ "Generated key %s but could not get it back immediately afterwards.",
+ alias));
+ }
+ return new RecoverableKeyStoreSecondaryKey(alias, key);
+ }
+
+ /**
+ * Removes the secondary key. This means the key will no longer be recoverable.
+ *
+ * @param alias The alias of the key.
+ * @throws InternalRecoveryServiceException if there was a {@link RecoveryController} error.
+ */
+ public void remove(String alias) throws InternalRecoveryServiceException {
+ mRecoveryController.removeKey(alias);
+ }
+
+ /**
+ * Returns the {@link RecoverableKeyStoreSecondaryKey} with {@code alias} if it is in the {@link
+ * RecoveryController}. Otherwise, {@link Optional#empty()}.
+ */
+ public Optional<RecoverableKeyStoreSecondaryKey> get(String alias)
+ throws InternalRecoveryServiceException, UnrecoverableKeyException {
+ SecretKey secretKey = (SecretKey) mRecoveryController.getKey(alias);
+ return Optional.ofNullable(secretKey)
+ .map(key -> new RecoverableKeyStoreSecondaryKey(alias, key));
+ }
+
+ /**
+ * Generates a new key alias. This has more entropy than a UUID - it can be considered
+ * universally unique.
+ */
+ private String generateId() {
+ byte[] id = new byte[BACKUP_KEY_SUFFIX_LENGTH_BITS / BITS_PER_BYTE];
+ mSecureRandom.nextBytes(id);
+ return BACKUP_KEY_ALIAS_PREFIX + ByteStringUtils.toHexString(id);
+ }
+
+ /** Constructs a {@link RecoverableKeyStoreSecondaryKeyManager}. */
+ public interface RecoverableKeyStoreSecondaryKeyManagerProvider {
+ /** Returns a newly constructed {@link RecoverableKeyStoreSecondaryKeyManager}. */
+ RecoverableKeyStoreSecondaryKeyManager get();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
new file mode 100644
index 0000000..ebf09df
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
@@ -0,0 +1,47 @@
+/*
+ * 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.server.backup.encryption.keys;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/** 256-bit AES key generator. Each app should have its own separate AES key. */
+public class TertiaryKeyGenerator {
+ private static final int KEY_SIZE_BITS = 256;
+ private static final String KEY_ALGORITHM = "AES";
+
+ private final KeyGenerator mKeyGenerator;
+
+ /** New instance generating keys using {@code secureRandom}. */
+ public TertiaryKeyGenerator(SecureRandom secureRandom) {
+ try {
+ mKeyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+ mKeyGenerator.init(KEY_SIZE_BITS, secureRandom);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(
+ "Impossible condition: JCE thinks it does not support AES.", e);
+ }
+ }
+
+ /** Generates a new random AES key. */
+ public SecretKey generate() {
+ return mKeyGenerator.generateKey();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
new file mode 100644
index 0000000..ec90f6c
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
@@ -0,0 +1,113 @@
+/*
+ * 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.server.backup.encryption.keys;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Locale;
+
+/**
+ * Tracks when a tertiary key rotation is due.
+ *
+ * <p>After a certain number of incremental backups, the device schedules a full backup, which will
+ * generate a new encryption key, effecting a key rotation. We should do this on a regular basis so
+ * that if a key does become compromised it has limited value to the attacker.
+ *
+ * <p>No additional synchronization of this class is provided. Only one instance should be used at
+ * any time. This should be fine as there should be no parallelism in backups.
+ */
+public class TertiaryKeyRotationTracker {
+ private static final int MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION = 31;
+ private static final String SHARED_PREFERENCES_NAME = "tertiary_key_rotation_tracker";
+
+ private static final String TAG = "TertiaryKeyRotationTracker";
+ private static final boolean DEBUG = false;
+
+ /**
+ * A new instance, using {@code context} to commit data to disk via {@link SharedPreferences}.
+ */
+ public static TertiaryKeyRotationTracker getInstance(Context context) {
+ return new TertiaryKeyRotationTracker(
+ context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE));
+ }
+
+ private final SharedPreferences mSharedPreferences;
+
+ /** New instance, storing data in {@code mSharedPreferences}. */
+ @VisibleForTesting
+ TertiaryKeyRotationTracker(SharedPreferences sharedPreferences) {
+ mSharedPreferences = sharedPreferences;
+ }
+
+ /**
+ * Returns {@code true} if the given app is due having its key rotated.
+ *
+ * @param packageName The package name of the app.
+ */
+ public boolean isKeyRotationDue(String packageName) {
+ return getBackupsSinceRotation(packageName) >= MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION;
+ }
+
+ /**
+ * Records that an incremental backup has occurred. Each incremental backup brings the app
+ * closer to the time when its key should be rotated.
+ *
+ * @param packageName The package name of the app for which the backup occurred.
+ */
+ public void recordBackup(String packageName) {
+ int backupsSinceRotation = getBackupsSinceRotation(packageName) + 1;
+ mSharedPreferences.edit().putInt(packageName, backupsSinceRotation).apply();
+ if (DEBUG) {
+ Slog.d(
+ TAG,
+ String.format(
+ Locale.US,
+ "Incremental backup for %s. %d backups until key rotation.",
+ packageName,
+ Math.max(
+ 0,
+ MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION
+ - backupsSinceRotation)));
+ }
+ }
+
+ /**
+ * Resets the rotation delay for the given app. Should be invoked after a key rotation.
+ *
+ * @param packageName Package name of the app whose key has rotated.
+ */
+ public void resetCountdown(String packageName) {
+ mSharedPreferences.edit().putInt(packageName, 0).apply();
+ }
+
+ /** Marks all enrolled packages for key rotation. */
+ public void markAllForRotation() {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ for (String packageName : mSharedPreferences.getAll().keySet()) {
+ editor.putInt(packageName, MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION);
+ }
+ editor.apply();
+ }
+
+ private int getBackupsSinceRotation(String packageName) {
+ return mSharedPreferences.getInt(packageName, 0);
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 2584187..9b9f4de 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -15,12 +15,15 @@
*/
package com.android.server.notification;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.provider.Settings;
import android.telecom.TelecomManager;
import com.android.internal.util.NotificationMessagingUtil;
@@ -47,6 +50,21 @@
@Override
public int compare(NotificationRecord left, NotificationRecord right) {
+ final int leftImportance = left.getImportance();
+ final int rightImportance = right.getImportance();
+ final boolean isLeftHighImportance = leftImportance >= IMPORTANCE_DEFAULT;
+ final boolean isRightHighImportance = rightImportance >= IMPORTANCE_DEFAULT;
+
+ // With new interruption model, prefer importance bucket above all other criteria
+ // (to ensure buckets are contiguous)
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
+ if (isLeftHighImportance != isRightHighImportance) {
+ // by importance bucket, high importance higher than low importance
+ return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
+ }
+ }
+
// first all colorized notifications
boolean leftImportantColorized = isImportantColorized(left);
boolean rightImportantColorized = isImportantColorized(right);
@@ -86,8 +104,6 @@
return -1 * Boolean.compare(leftPeople, rightPeople);
}
- final int leftImportance = left.getImportance();
- final int rightImportance = right.getImportance();
if (leftImportance != rightImportance) {
// by importance, high to low
return -1 * Integer.compare(leftImportance, rightImportance);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 25a1626..95e1962 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -207,7 +207,6 @@
import com.android.internal.util.XmlUtils;
import com.android.server.DeviceIdleController;
import com.android.server.EventLogTags;
-import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.lights.Light;
@@ -266,7 +265,7 @@
// message codes
static final int MESSAGE_DURATION_REACHED = 2;
- // 3: removed to a different handler
+ static final int MESSAGE_SAVE_POLICY_FILE = 3;
static final int MESSAGE_SEND_RANKING_UPDATE = 4;
static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
@@ -574,7 +573,7 @@
mListeners.migrateToXml();
mAssistants.migrateToXml();
mConditionProviders.migrateToXml();
- handleSavePolicyFile();
+ savePolicyFile();
}
mAssistants.ensureAssistant();
@@ -604,28 +603,34 @@
}
}
- private void handleSavePolicyFile() {
- IoThread.getHandler().post(() -> {
- if (DBG) Slog.d(TAG, "handleSavePolicyFile");
- synchronized (mPolicyFile) {
- final FileOutputStream stream;
- try {
- stream = mPolicyFile.startWrite();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file", e);
- return;
- }
+ /**
+ * Saves notification policy
+ */
+ public void savePolicyFile() {
+ mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
+ mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
+ }
- try {
- writePolicyXml(stream, false /*forBackup*/);
- mPolicyFile.finishWrite(stream);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file, restoring backup", e);
- mPolicyFile.failWrite(stream);
- }
+ private void handleSavePolicyFile() {
+ if (DBG) Slog.d(TAG, "handleSavePolicyFile");
+ synchronized (mPolicyFile) {
+ final FileOutputStream stream;
+ try {
+ stream = mPolicyFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file", e);
+ return;
}
- BackupManager.dataChanged(getContext().getPackageName());
- });
+
+ try {
+ writePolicyXml(stream, false /*forBackup*/);
+ mPolicyFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file, restoring backup", e);
+ mPolicyFile.failWrite(stream);
+ }
+ }
+ BackupManager.dataChanged(getContext().getPackageName());
}
private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
@@ -1133,8 +1138,8 @@
}
}
-
mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
+ savePolicyFile();
}
}
};
@@ -1201,7 +1206,7 @@
mListeners.onUserRemoved(userId);
mConditionProviders.onUserRemoved(userId);
mAssistants.onUserRemoved(userId);
- handleSavePolicyFile();
+ savePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
@@ -1457,7 +1462,7 @@
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
- handleSavePolicyFile();
+ savePolicyFile();
}
@Override
@@ -1759,7 +1764,7 @@
modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
}
- handleSavePolicyFile();
+ savePolicyFile();
}
private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel preUpdate,
@@ -2227,7 +2232,7 @@
Slog.w(TAG, "Can't notify app about app block change", e);
}
- handleSavePolicyFile();
+ savePolicyFile();
}
/**
@@ -2284,7 +2289,7 @@
public void setShowBadge(String pkg, int uid, boolean showBadge) {
checkCallerIsSystem();
mPreferencesHelper.setShowBadge(pkg, uid, showBadge);
- handleSavePolicyFile();
+ savePolicyFile();
}
@Override
@@ -2300,7 +2305,7 @@
if (info != null) {
mPreferencesHelper.setNotificationDelegate(
callingPkg, callingUid, delegate, info.uid);
- handleSavePolicyFile();
+ savePolicyFile();
}
} catch (RemoteException e) {
// :(
@@ -2311,7 +2316,7 @@
public void revokeNotificationDelegate(String callingPkg) {
checkCallerIsSameApp(callingPkg);
mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid());
- handleSavePolicyFile();
+ savePolicyFile();
}
@Override
@@ -2346,7 +2351,7 @@
NotificationChannelGroup group) throws RemoteException {
enforceSystemOrSystemUI("Caller not system or systemui");
createNotificationChannelGroup(pkg, uid, group, false, false);
- handleSavePolicyFile();
+ savePolicyFile();
}
@Override
@@ -2359,7 +2364,7 @@
final NotificationChannelGroup group = groups.get(i);
createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true, false);
}
- handleSavePolicyFile();
+ savePolicyFile();
}
private void createNotificationChannelsImpl(String pkg, int uid,
@@ -2377,7 +2382,7 @@
mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
}
- handleSavePolicyFile();
+ savePolicyFile();
}
@Override
@@ -2422,7 +2427,7 @@
UserHandle.getUserHandleForUid(callingUid),
mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- handleSavePolicyFile();
+ savePolicyFile();
}
@Override
@@ -2464,7 +2469,7 @@
mListeners.notifyNotificationChannelGroupChanged(
pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- handleSavePolicyFile();
+ savePolicyFile();
}
}
@@ -2597,7 +2602,7 @@
true, UserHandle.getCallingUserId(), packages, uids);
}
- handleSavePolicyFile();
+ savePolicyFile();
}
@@ -3385,7 +3390,7 @@
final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
try {
readPolicyXml(bais, true /*forRestore*/);
- handleSavePolicyFile();
+ savePolicyFile();
} catch (NumberFormatException | XmlPullParserException | IOException e) {
Slog.w(TAG, "applyRestore: error reading payload", e);
}
@@ -3426,7 +3431,7 @@
.setPackage(pkg)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- handleSavePolicyFile();
+ savePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3570,7 +3575,7 @@
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- handleSavePolicyFile();
+ savePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3596,7 +3601,7 @@
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- handleSavePolicyFile();
+ savePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3679,7 +3684,7 @@
verifyPrivilegedListener(token, user, false);
createNotificationChannelGroup(
pkg, getUidForPackageAndUser(pkg, user), group, false, true);
- handleSavePolicyFile();
+ savePolicyFile();
}
@Override
@@ -3728,7 +3733,7 @@
}
if (allow != mLockScreenAllowSecureNotifications) {
mLockScreenAllowSecureNotifications = allow;
- handleSavePolicyFile();
+ savePolicyFile();
}
}
@@ -4487,7 +4492,7 @@
Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
}
mSnoozeHelper.update(userId, r);
- handleSavePolicyFile();
+ savePolicyFile();
return false;
}
@@ -4618,7 +4623,7 @@
mSnoozeHelper.snooze(r, mDuration);
}
r.recordSnoozed();
- handleSavePolicyFile();
+ savePolicyFile();
}
}
@@ -4696,7 +4701,7 @@
if (mReason != REASON_SNOOZED) {
final boolean wasSnoozed = mSnoozeHelper.cancel(mUserId, mPkg, mTag, mId);
if (wasSnoozed) {
- handleSavePolicyFile();
+ savePolicyFile();
}
}
}
@@ -5740,6 +5745,9 @@
case MESSAGE_FINISH_TOKEN_TIMEOUT:
handleKillTokenTimeout((ToastRecord) msg.obj);
break;
+ case MESSAGE_SAVE_POLICY_FILE:
+ handleSavePolicyFile();
+ break;
case MESSAGE_SEND_RANKING_UPDATE:
handleSendRankingUpdate();
break;
@@ -6257,7 +6265,7 @@
Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
}
mSnoozeHelper.repost(key);
- handleSavePolicyFile();
+ savePolicyFile();
}
@GuardedBy("mNotificationLock")
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0967afd..37db671 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -863,6 +863,13 @@
*/
Configuration getGlobalConfigurationForCallingPid() {
final int pid = Binder.getCallingPid();
+ return getGlobalConfigurationForPid(pid);
+ }
+
+ /**
+ * Return the global configuration used by the process corresponding to the given pid.
+ */
+ Configuration getGlobalConfigurationForPid(int pid) {
if (pid == MY_PID || pid < 0) {
return getGlobalConfiguration();
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 64553a8..e779d1b 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -319,6 +319,13 @@
onOverrideConfigurationChanged(mTmpConfig);
}
+ /** Sets the windowing mode for the configuration container. */
+ void setDisplayWindowingMode(int windowingMode) {
+ mTmpConfig.setTo(getOverrideConfiguration());
+ mTmpConfig.windowConfiguration.setDisplayWindowingMode(windowingMode);
+ onOverrideConfigurationChanged(mTmpConfig);
+ }
+
/**
* Returns true if this container is currently in multi-window mode. I.e. sharing the screen
* with another activity.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 05e8267..aba2eb3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1546,6 +1546,7 @@
final int dh = displayInfo.logicalHeight;
config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
config.windowConfiguration.setWindowingMode(getWindowingMode());
+ config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
config.windowConfiguration.setRotation(displayInfo.rotation);
final float density = mDisplayMetrics.density;
@@ -1953,6 +1954,17 @@
mWmService.mWindowsChanged = true;
}
+ @Override
+ public void setWindowingMode(int windowingMode) {
+ super.setWindowingMode(windowingMode);
+ super.setDisplayWindowingMode(windowingMode);
+ }
+
+ @Override
+ void setDisplayWindowingMode(int windowingMode) {
+ setWindowingMode(windowingMode);
+ }
+
/**
* In split-screen mode we process the IME containers above the docked divider
* rather than directly above their target.
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index e83b863..9f1a587 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ClipData;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
@@ -449,11 +448,4 @@
* Return the display Id for given window.
*/
public abstract int getDisplayIdForWindow(IBinder windowToken);
-
- // TODO: use WindowProcessController once go/wm-unified is done.
- /**
- * Notifies the window manager that configuration of the process associated with the input pid
- * changed.
- */
- public abstract void onProcessConfigurationChanged(int pid, Configuration newConfig);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 52b24b3..002d6d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -735,6 +735,7 @@
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
final DisplayManager mDisplayManager;
+ final ActivityTaskManagerService mAtmService;
// Indicates whether this device supports wide color gamut / HDR rendering
private boolean mHasWideColorGamutSupport;
@@ -897,11 +898,10 @@
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
- final WindowManagerGlobalLock globalLock) {
+ ActivityTaskManagerService atm) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
- globalLock),
- 0);
+ atm), 0);
return sInstance;
}
@@ -923,9 +923,10 @@
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
- WindowManagerGlobalLock globalLock) {
+ ActivityTaskManagerService atm) {
installLock(this, INDEX_WINDOW);
- mGlobalLock = globalLock;
+ mGlobalLock = atm.getGlobalLock();
+ mAtmService = atm;
mContext = context;
mAllowBootMessages = showBootMsgs;
mOnlyCore = onlyCore;
@@ -7281,19 +7282,6 @@
return Display.INVALID_DISPLAY;
}
}
-
- @Override
- public void onProcessConfigurationChanged(int pid, Configuration newConfig) {
- synchronized (mGlobalLock) {
- Configuration currentConfig = mProcessConfigurations.get(pid);
- if (currentConfig == null) {
- currentConfig = new Configuration(newConfig);
- } else {
- currentConfig.setTo(newConfig);
- }
- mProcessConfigurations.put(pid, currentConfig);
- }
- }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5cc3623..31e0d83 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2263,8 +2263,10 @@
// For child windows we want to use the pid for the parent window in case the the child
// window was added from another process.
final int pid = getParentWindow() != null ? getParentWindow().mSession.mPid : mSession.mPid;
- mTempConfiguration.setTo(mWmService.mProcessConfigurations.get(
- pid, mWmService.mRoot.getConfiguration()));
+ final Configuration processConfig =
+ mWmService.mAtmService.getGlobalConfigurationForPid(pid);
+ mTempConfiguration.setTo(processConfig == null
+ ? mWmService.mRoot.getConfiguration() : processConfig);
return mTempConfiguration;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 05ff660..c4d2a91 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -926,7 +926,7 @@
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
- new PhoneWindowManager(), mWindowManagerGlobalLock);
+ new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
new file mode 100644
index 0000000..5342efa
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.security.SecureRandom;
+import java.util.Optional;
+
+/** Tests for {@link RecoverableKeyStoreSecondaryKeyManager}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class})
+public class RecoverableKeyStoreSecondaryKeyManagerTest {
+ private static final String BACKUP_KEY_ALIAS_PREFIX =
+ "com.android.server.backup/recoverablekeystore/";
+ private static final int BITS_PER_BYTE = 8;
+ private static final int BACKUP_KEY_SUFFIX_LENGTH_BYTES = 128 / BITS_PER_BYTE;
+ private static final int HEX_PER_BYTE = 2;
+ private static final int BACKUP_KEY_ALIAS_LENGTH =
+ BACKUP_KEY_ALIAS_PREFIX.length() + BACKUP_KEY_SUFFIX_LENGTH_BYTES * HEX_PER_BYTE;
+ private static final String NONEXISTENT_KEY_ALIAS = "NONEXISTENT_KEY_ALIAS";
+
+ private RecoverableKeyStoreSecondaryKeyManager mRecoverableKeyStoreSecondaryKeyManager;
+ private Context mContext;
+
+ /** Create a new {@link RecoverableKeyStoreSecondaryKeyManager} to use in tests. */
+ @Before
+ public void setUp() throws Exception {
+ mContext = RuntimeEnvironment.application;
+
+ mRecoverableKeyStoreSecondaryKeyManager =
+ new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(mContext), new SecureRandom());
+ }
+
+ /** Reset the {@link ShadowRecoveryController}. */
+ @After
+ public void tearDown() throws Exception {
+ ShadowRecoveryController.reset();
+ }
+
+ /** The generated key should always have the prefix {@code BACKUP_KEY_ALIAS_PREFIX}. */
+ @Test
+ public void generate_generatesKeyWithExpectedPrefix() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+ assertThat(key.getAlias()).startsWith(BACKUP_KEY_ALIAS_PREFIX);
+ }
+
+ /** The generated key should always have length {@code BACKUP_KEY_ALIAS_LENGTH}. */
+ @Test
+ public void generate_generatesKeyWithExpectedLength() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+ assertThat(key.getAlias()).hasLength(BACKUP_KEY_ALIAS_LENGTH);
+ }
+
+ /** Ensure that hidden API exceptions are rethrown when generating keys. */
+ @Test
+ public void generate_encounteringHiddenApiException_rethrowsException() {
+ ShadowRecoveryController.setThrowsInternalError(true);
+
+ assertThrows(
+ InternalRecoveryServiceException.class,
+ mRecoverableKeyStoreSecondaryKeyManager::generate);
+ }
+
+ /** Ensure that retrieved keys correspond to those generated earlier. */
+ @Test
+ public void get_getsKeyGeneratedByController() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+ Optional<RecoverableKeyStoreSecondaryKey> retrievedKey =
+ mRecoverableKeyStoreSecondaryKeyManager.get(key.getAlias());
+
+ assertThat(retrievedKey.isPresent()).isTrue();
+ assertThat(retrievedKey.get().getAlias()).isEqualTo(key.getAlias());
+ assertThat(retrievedKey.get().getSecretKey()).isEqualTo(key.getSecretKey());
+ }
+
+ /**
+ * Ensure that a call to {@link RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)}
+ * for nonexistent aliases returns an emtpy {@link Optional}.
+ */
+ @Test
+ public void get_forNonExistentKey_returnsEmptyOptional() throws Exception {
+ Optional<RecoverableKeyStoreSecondaryKey> retrievedKey =
+ mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS);
+
+ assertThat(retrievedKey.isPresent()).isFalse();
+ }
+
+ /**
+ * Ensure that exceptions occurring during {@link
+ * RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)} are not rethrown.
+ */
+ @Test
+ public void get_encounteringInternalException_doesNotPropagateException() throws Exception {
+ ShadowRecoveryController.setThrowsInternalError(true);
+
+ // Should not throw exception
+ mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS);
+ }
+
+ /** Ensure that keys are correctly removed from the store. */
+ @Test
+ public void remove_removesKeyFromRecoverableStore() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+ mRecoverableKeyStoreSecondaryKeyManager.remove(key.getAlias());
+
+ assertThat(RecoveryController.getInstance(mContext).getAliases())
+ .doesNotContain(key.getAlias());
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
new file mode 100644
index 0000000..89977f8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey.Status;
+import com.android.server.backup.testing.CryptoTestUtils;
+import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import javax.crypto.SecretKey;
+
+/** Tests for {@link RecoverableKeyStoreSecondaryKey}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class})
+public class RecoverableKeyStoreSecondaryKeyTest {
+ private static final String TEST_ALIAS = "test";
+ private static final int NONEXISTENT_STATUS_CODE = 42;
+
+ private RecoverableKeyStoreSecondaryKey mSecondaryKey;
+ private SecretKey mGeneratedSecretKey;
+ private Context mContext;
+
+ /** Instantiate a {@link RecoverableKeyStoreSecondaryKey} to use in tests. */
+ @Before
+ public void setUp() throws Exception {
+ mContext = RuntimeEnvironment.application;
+ mGeneratedSecretKey = CryptoTestUtils.generateAesKey();
+ mSecondaryKey = new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, mGeneratedSecretKey);
+ }
+
+ /** Reset the {@link ShadowRecoveryController}. */
+ @After
+ public void tearDown() throws Exception {
+ ShadowRecoveryController.reset();
+ }
+
+ /**
+ * Checks that {@link RecoverableKeyStoreSecondaryKey#getAlias()} returns the value supplied in
+ * the constructor.
+ */
+ @Test
+ public void getAlias() {
+ String alias = mSecondaryKey.getAlias();
+
+ assertThat(alias).isEqualTo(TEST_ALIAS);
+ }
+
+ /**
+ * Checks that {@link RecoverableKeyStoreSecondaryKey#getSecretKey()} returns the value supplied
+ * in the constructor.
+ */
+ @Test
+ public void getSecretKey() {
+ SecretKey secretKey = mSecondaryKey.getSecretKey();
+
+ assertThat(secretKey).isEqualTo(mGeneratedSecretKey);
+ }
+
+ /**
+ * Checks that passing a secret key that is null to the constructor throws an exception.
+ */
+ @Test
+ public void constructor_withNullSecretKey_throwsNullPointerException() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, null));
+ }
+
+ /**
+ * Checks that passing an alias that is null to the constructor throws an exception.
+ */
+ @Test
+ public void constructor_withNullAlias_throwsNullPointerException() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new RecoverableKeyStoreSecondaryKey(null, mGeneratedSecretKey));
+ }
+
+ /** Checks that the synced status is returned correctly. */
+ @Test
+ public void getStatus_whenSynced_returnsSynced() throws Exception {
+ setStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.SYNCED);
+ }
+
+ /** Checks that the in progress sync status is returned correctly. */
+ @Test
+ public void getStatus_whenNotSynced_returnsNotSynced() throws Exception {
+ setStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.NOT_SYNCED);
+ }
+
+ /** Checks that the failure status is returned correctly. */
+ @Test
+ public void getStatus_onPermanentFailure_returnsDestroyed() throws Exception {
+ setStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.DESTROYED);
+ }
+
+ /** Checks that an unknown status results in {@code NOT_SYNCED} being returned. */
+ @Test
+ public void getStatus_forUnknownStatusCode_returnsNotSynced() throws Exception {
+ setStatus(NONEXISTENT_STATUS_CODE);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.NOT_SYNCED);
+ }
+
+ /** Checks that an internal error results in {@code NOT_SYNCED} being returned. */
+ @Test
+ public void getStatus_onInternalError_returnsNotSynced() throws Exception {
+ ShadowRecoveryController.setThrowsInternalError(true);
+
+ int status = mSecondaryKey.getStatus(mContext);
+
+ assertThat(status).isEqualTo(Status.NOT_SYNCED);
+ }
+
+ private void setStatus(int status) throws Exception {
+ ShadowRecoveryController.setRecoveryStatus(TEST_ALIAS, status);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
new file mode 100644
index 0000000..48216f8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.security.SecureRandom;
+
+import javax.crypto.SecretKey;
+
+/** Tests for {@link TertiaryKeyGenerator}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class TertiaryKeyGeneratorTest {
+ private static final String KEY_ALGORITHM = "AES";
+ private static final int KEY_SIZE_BITS = 256;
+
+ private TertiaryKeyGenerator mTertiaryKeyGenerator;
+
+ /** Instantiate a new {@link TertiaryKeyGenerator} for use in tests. */
+ @Before
+ public void setUp() {
+ mTertiaryKeyGenerator = new TertiaryKeyGenerator(new SecureRandom());
+ }
+
+ /** Generated keys should be AES keys. */
+ @Test
+ public void generate_generatesAESKeys() {
+ SecretKey secretKey = mTertiaryKeyGenerator.generate();
+
+ assertThat(secretKey.getAlgorithm()).isEqualTo(KEY_ALGORITHM);
+ }
+
+ /** Generated keys should be 256 bits in size. */
+ @Test
+ public void generate_generates256BitKeys() {
+ SecretKey secretKey = mTertiaryKeyGenerator.generate();
+
+ assertThat(secretKey.getEncoded()).hasLength(KEY_SIZE_BITS / 8);
+ }
+
+ /**
+ * Subsequent calls to {@link TertiaryKeyGenerator#generate()} should generate different keys.
+ */
+ @Test
+ public void generate_generatesNewKeys() {
+ SecretKey key1 = mTertiaryKeyGenerator.generate();
+ SecretKey key2 = mTertiaryKeyGenerator.generate();
+
+ assertThat(key1).isNotEqualTo(key2);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
new file mode 100644
index 0000000..49bb410
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+/** Tests for {@link TertiaryKeyRotationTracker}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class TertiaryKeyRotationTrackerTest {
+ private static final String PACKAGE_1 = "com.package.one";
+ private static final int NUMBER_OF_BACKUPS_BEFORE_ROTATION = 31;
+
+ private TertiaryKeyRotationTracker mTertiaryKeyRotationTracker;
+
+ /** Instantiate a {@link TertiaryKeyRotationTracker} for use in tests. */
+ @Before
+ public void setUp() {
+ mTertiaryKeyRotationTracker = newInstance();
+ }
+
+ /** New packages should not be due for key rotation. */
+ @Test
+ public void isKeyRotationDue_forNewPackage_isFalse() {
+ // Simulate a new package by not calling simulateBackups(). As a result, PACKAGE_1 hasn't
+ // been seen by mTertiaryKeyRotationTracker before.
+ boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
+
+ assertThat(keyRotationDue).isFalse();
+ }
+
+ /**
+ * Key rotation should not be due after less than {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION}
+ * backups.
+ */
+ @Test
+ public void isKeyRotationDue_afterLessThanRotationAmountBackups_isFalse() {
+ simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION - 1);
+
+ boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
+
+ assertThat(keyRotationDue).isFalse();
+ }
+
+ /** Key rotation should be due after {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION} backups. */
+ @Test
+ public void isKeyRotationDue_afterRotationAmountBackups_isTrue() {
+ simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
+
+ boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
+
+ assertThat(keyRotationDue).isTrue();
+ }
+
+ /**
+ * A call to {@link TertiaryKeyRotationTracker#resetCountdown(String)} should make sure no key
+ * rotation is due.
+ */
+ @Test
+ public void resetCountdown_makesKeyRotationNotDue() {
+ simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
+
+ mTertiaryKeyRotationTracker.resetCountdown(PACKAGE_1);
+
+ assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse();
+ }
+
+ /**
+ * New instances of {@link TertiaryKeyRotationTracker} should read state about the number of
+ * backups from disk.
+ */
+ @Test
+ public void isKeyRotationDue_forNewInstance_readsStateFromDisk() {
+ simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
+
+ boolean keyRotationDueForNewInstance = newInstance().isKeyRotationDue(PACKAGE_1);
+
+ assertThat(keyRotationDueForNewInstance).isTrue();
+ }
+
+ /**
+ * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should mark all previously
+ * seen packages for rotation.
+ */
+ @Test
+ public void markAllForRotation_marksSeenPackagesForKeyRotation() {
+ simulateBackups(PACKAGE_1, /*numberOfBackups=*/ 1);
+
+ mTertiaryKeyRotationTracker.markAllForRotation();
+
+ assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isTrue();
+ }
+
+ /**
+ * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should not mark any new
+ * packages for rotation.
+ */
+ @Test
+ public void markAllForRotation_doesNotMarkUnseenPackages() {
+ mTertiaryKeyRotationTracker.markAllForRotation();
+
+ assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse();
+ }
+
+ private void simulateBackups(String packageName, int numberOfBackups) {
+ while (numberOfBackups > 0) {
+ mTertiaryKeyRotationTracker.recordBackup(packageName);
+ numberOfBackups--;
+ }
+ }
+
+ private static TertiaryKeyRotationTracker newInstance() {
+ return TertiaryKeyRotationTracker.getInstance(RuntimeEnvironment.application);
+ }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java b/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
new file mode 100644
index 0000000..9c06d81
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.server.testing.shadows;
+
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/** Shadow {@link InternalRecoveryServiceException}. */
+@Implements(InternalRecoveryServiceException.class)
+public class ShadowInternalRecoveryServiceException {
+ private String mMessage;
+
+ @Implementation
+ public void __constructor__(String message) {
+ mMessage = message;
+ }
+
+ @Implementation
+ public void __constructor__(String message, Throwable cause) {
+ mMessage = message;
+ }
+
+ @Implementation
+ public String getMessage() {
+ return mMessage;
+ }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java b/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java
new file mode 100644
index 0000000..7dad8a4
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java
@@ -0,0 +1,152 @@
+/*
+ * 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.server.testing.shadows;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.google.common.collect.ImmutableList;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.lang.reflect.Constructor;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.crypto.KeyGenerator;
+
+/**
+ * Shadow of {@link RecoveryController}.
+ *
+ * <p>Instead of generating keys via the {@link RecoveryController}, this shadow generates them in
+ * memory.
+ */
+@Implements(RecoveryController.class)
+public class ShadowRecoveryController {
+ private static final String KEY_GENERATOR_ALGORITHM = "AES";
+ private static final int KEY_SIZE_BITS = 256;
+
+ private static boolean sIsSupported = true;
+ private static boolean sThrowsInternalError = false;
+ private static HashMap<String, Key> sKeysByAlias = new HashMap<>();
+ private static HashMap<String, Integer> sKeyStatusesByAlias = new HashMap<>();
+
+ @Implementation
+ public void __constructor__() {
+ // do not throw
+ }
+
+ @Implementation
+ public static RecoveryController getInstance(Context context) {
+ // Call non-public constructor.
+ try {
+ Constructor<RecoveryController> constructor = RecoveryController.class.getConstructor();
+ return constructor.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Implementation
+ public static boolean isRecoverableKeyStoreEnabled(Context context) {
+ return sIsSupported;
+ }
+
+ @Implementation
+ public Key generateKey(String alias)
+ throws InternalRecoveryServiceException, LockScreenRequiredException {
+ maybeThrowError();
+ KeyGenerator keyGenerator;
+ try {
+ keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
+ } catch (NoSuchAlgorithmException e) {
+ // Should never happen
+ throw new RuntimeException(e);
+ }
+
+ keyGenerator.init(KEY_SIZE_BITS);
+ Key key = keyGenerator.generateKey();
+ sKeysByAlias.put(alias, key);
+ sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ return key;
+ }
+
+ @Implementation
+ public Key getKey(String alias)
+ throws InternalRecoveryServiceException, UnrecoverableKeyException {
+ return sKeysByAlias.get(alias);
+ }
+
+ @Implementation
+ public void removeKey(String alias) throws InternalRecoveryServiceException {
+ sKeyStatusesByAlias.remove(alias);
+ sKeysByAlias.remove(alias);
+ }
+
+ @Implementation
+ public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
+ maybeThrowError();
+ return sKeyStatusesByAlias.getOrDefault(
+ alias, RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+ }
+
+ @Implementation
+ public List<String> getAliases() throws InternalRecoveryServiceException {
+ return ImmutableList.copyOf(sKeyStatusesByAlias.keySet());
+ }
+
+ private static void maybeThrowError() throws InternalRecoveryServiceException {
+ if (sThrowsInternalError) {
+ throw new InternalRecoveryServiceException("test error");
+ }
+ }
+
+ /** Sets the recovery status of the key with {@code alias} to {@code status}. */
+ public static void setRecoveryStatus(String alias, int status) {
+ sKeyStatusesByAlias.put(alias, status);
+ }
+
+ /** Sets all existing keys to being synced. */
+ public static void syncAllKeys() {
+ for (String alias : sKeysByAlias.keySet()) {
+ sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNCED);
+ }
+ }
+
+ public static void setThrowsInternalError(boolean throwsInternalError) {
+ ShadowRecoveryController.sThrowsInternalError = throwsInternalError;
+ }
+
+ public static void setIsSupported(boolean isSupported) {
+ ShadowRecoveryController.sIsSupported = isSupported;
+ }
+
+ @Resetter
+ public static void reset() {
+ sIsSupported = true;
+ sThrowsInternalError = false;
+ sKeysByAlias.clear();
+ sKeyStatusesByAlias.clear();
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index b30bb4b..0681295 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -15,8 +15,9 @@
*/
package com.android.server.notification;
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@@ -34,8 +35,8 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import android.telecom.TelecomManager;
import android.support.test.runner.AndroidJUnit4;
+import android.telecom.TelecomManager;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.server.UiServiceTestCase;
@@ -211,7 +212,7 @@
mRecordColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "colorized", uid2, uid2, n13,
new UserHandle(userId), "", 1999), getDefaultChannel());
- mRecordHighCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
+ mRecordColorized.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
Notification n14 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setCategory(Notification.CATEGORY_CALL)
@@ -225,11 +226,11 @@
}
@Test
- public void testOrdering() throws Exception {
+ public void testOrdering() {
final List<NotificationRecord> expected = new ArrayList<>();
expected.add(mRecordColorizedCall);
- expected.add(mRecordDefaultMedia);
expected.add(mRecordColorized);
+ expected.add(mRecordDefaultMedia);
expected.add(mRecordHighCall);
expected.add(mRecordInlineReply);
if (mRecordSms != null) {
@@ -250,11 +251,11 @@
Collections.sort(actual, new NotificationComparator(mContext));
- assertEquals(expected, actual);
+ assertThat(actual, contains(expected.toArray()));
}
@Test
- public void testMessaging() throws Exception {
+ public void testMessaging() {
NotificationComparator comp = new NotificationComparator(mContext);
assertTrue(comp.isImportantMessaging(mRecordInlineReply));
if (mRecordSms != null) {
@@ -265,7 +266,7 @@
}
@Test
- public void testPeople() throws Exception {
+ public void testPeople() {
NotificationComparator comp = new NotificationComparator(mContext);
assertTrue(comp.isImportantPeople(mRecordStarredContact));
assertTrue(comp.isImportantPeople(mRecordContact));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 266d884..50fd188 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -30,6 +30,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
import android.app.ActivityManagerInternal;
import android.content.Context;
@@ -125,11 +126,12 @@
if (input != null && input.length > 1) {
doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
}
+ ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
+ when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock());
mService = WindowManagerService.main(context, ims, false, false,
mPolicy = new TestWindowManagerPolicy(
- WindowManagerServiceRule.this::getWindowManagerService),
- new WindowManagerGlobalLock());
+ WindowManagerServiceRule.this::getWindowManagerService), atms);
mService.mTransactionFactory = () -> {
final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
mSurfaceTransactions.add(new WeakReference<>(transaction));
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2a01ac4..2c06c47 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1590,14 +1590,23 @@
return subId;
}
- /** @hide */
- @UnsupportedAppUsage
- public void setDefaultSmsSubId(int subId) {
- if (VDBG) logd("setDefaultSmsSubId sub id = " + subId);
+ /**
+ * Set the subscription which will be used by default for SMS, with the subscription which
+ * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied
+ * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}).
+ *
+ * @param subscriptionId the supplied subscription ID
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDefaultSmsSubId(int subscriptionId) {
+ if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId);
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- iSub.setDefaultSmsSubId(subId);
+ iSub.setDefaultSmsSubId(subscriptionId);
}
} catch (RemoteException ex) {
// ignore it
@@ -1645,14 +1654,23 @@
return subId;
}
- /** @hide */
- @UnsupportedAppUsage
- public void setDefaultDataSubId(int subId) {
- if (VDBG) logd("setDataSubscription sub id = " + subId);
+ /**
+ * Set the subscription which will be used by default for data, with the subscription which
+ * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied
+ * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}).
+ *
+ * @param subscriptionId the supplied subscription ID
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDefaultDataSubId(int subscriptionId) {
+ if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- iSub.setDefaultDataSubId(subId);
+ iSub.setDefaultDataSubId(subscriptionId);
}
} catch (RemoteException ex) {
// ignore it
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 45d914e..fa9b76d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5541,19 +5541,40 @@
public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis,
@NonNull @CallbackExecutor Executor executor,
@NonNull NumberVerificationCallback callback) {
+ if (executor == null) {
+ throw new NullPointerException("Executor must be non-null");
+ }
+ if (callback == null) {
+ throw new NullPointerException("Callback must be non-null");
+ }
+
INumberVerificationCallback internalCallback = new INumberVerificationCallback.Stub() {
@Override
- public void onCallReceived(String phoneNumber) throws RemoteException {
- Binder.withCleanCallingIdentity(() -> callback.onCallReceived(phoneNumber));
+ public void onCallReceived(String phoneNumber) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onCallReceived(phoneNumber)));
}
@Override
- public void onVerificationFailed(int reason) throws RemoteException {
- Binder.withCleanCallingIdentity(() -> callback.onVerificationFailed(reason));
+ public void onVerificationFailed(int reason) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onVerificationFailed(reason)));
}
};
- // TODO -- call the aidl method
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.requestNumberVerification(range, timeoutMillis, internalCallback,
+ getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "requestNumberVerification RemoteException", ex);
+ executor.execute(() ->
+ callback.onVerificationFailed(NumberVerificationCallback.REASON_UNSPECIFIED));
+ }
}
/**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 32e939a0..399dc52 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -35,6 +35,7 @@
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
+import android.telephony.PhoneNumberRange;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -49,6 +50,7 @@
import android.telephony.ims.aidl.IImsRegistrationCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
+import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.OperatorInfo;
import java.util.List;
@@ -871,6 +873,17 @@
String getCdmaMin(int subId);
/**
+ * Request that the next incoming call from a number matching {@code range} be intercepted.
+ * @param range The range of phone numbers the caller expects a phone call from.
+ * @param timeoutMillis The amount of time to wait for such a call, or
+ * {@link #MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS}, whichever is lesser.
+ * @param callback the callback aidl
+ * @param callingPackage the calling package name.
+ */
+ void requestNumberVerification(in PhoneNumberRange range, long timeoutMillis,
+ in INumberVerificationCallback callback, String callingPackage);
+
+ /**
* Has the calling application been granted special privileges by the carrier.
*
* If any of the packages in the calling UID has carrier privileges, the
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index b0ed110..e0442f2 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -22,7 +22,6 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -1695,7 +1694,6 @@
* @param listener for callback on success or failure. Can be null.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void factoryReset(@NonNull Channel c, @Nullable ActionListener listener) {
checkChannel(c);