Merge "Shows a Toast when manual autofill request cannot be fulfilled." into oc-dev
diff --git a/Android.mk b/Android.mk
index 01fb73e..915f103 100644
--- a/Android.mk
+++ b/Android.mk
@@ -321,6 +321,8 @@
core/java/android/service/wallpaper/IWallpaperService.aidl \
core/java/android/service/chooser/IChooserTargetService.aidl \
core/java/android/service/chooser/IChooserTargetResult.aidl \
+ core/java/android/service/resolver/IResolverRankerService.aidl \
+ core/java/android/service/resolver/IResolverRankerResult.aidl \
core/java/android/text/ITextClassificationService.aidl \
core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
@@ -729,6 +731,7 @@
frameworks/base/core/java/android/service/notification/SnoozeCriterion.aidl \
frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
+ frameworks/base/core/java/android/service/resolver/ResolverTarget.aidl \
frameworks/base/core/java/android/speech/tts/Voice.aidl \
frameworks/base/core/java/android/app/usage/CacheQuotaHint.aidl \
frameworks/base/core/java/android/app/usage/ExternalStorageStats.aidl \
diff --git a/api/current.txt b/api/current.txt
index 48f57b4..fe35f61 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1609,6 +1609,7 @@
field public static final int alert_light_frame = 17301505; // 0x1080001
field public static final int arrow_down_float = 17301506; // 0x1080002
field public static final int arrow_up_float = 17301507; // 0x1080003
+ field public static final int autofilled_highlight = 17301684; // 0x10800b4
field public static final int bottom_bar = 17301658; // 0x108009a
field public static final int btn_default = 17301508; // 0x1080004
field public static final int btn_default_small = 17301509; // 0x1080005
@@ -5116,6 +5117,7 @@
method public java.lang.String getChannel();
method public java.lang.String getGroup();
method public android.graphics.drawable.Icon getLargeIcon();
+ method public java.lang.CharSequence getSettingsText();
method public java.lang.String getShortcutId();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
@@ -5160,6 +5162,8 @@
field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+ field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+ field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
field public static final java.lang.String EXTRA_PEOPLE = "android.people";
field public static final java.lang.String EXTRA_PICTURE = "android.picture";
field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
@@ -5345,6 +5349,7 @@
method public android.app.Notification.Builder setProgress(int, int, boolean);
method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+ method public android.app.Notification.Builder setSettingsText(java.lang.CharSequence);
method public android.app.Notification.Builder setShortcutId(java.lang.String);
method public android.app.Notification.Builder setShowWhen(boolean);
method public android.app.Notification.Builder setSmallIcon(int);
@@ -10802,6 +10807,7 @@
field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+ field public static final int PROTECTION_FLAG_RUNTIME_ONLY = 8192; // 0x2000
field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
diff --git a/api/system-current.txt b/api/system-current.txt
index bc063b5..e166ead 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -53,6 +53,7 @@
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
+ field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE";
field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
@@ -1727,6 +1728,7 @@
field public static final int alert_light_frame = 17301505; // 0x1080001
field public static final int arrow_down_float = 17301506; // 0x1080002
field public static final int arrow_up_float = 17301507; // 0x1080003
+ field public static final int autofilled_highlight = 17301684; // 0x10800b4
field public static final int bottom_bar = 17301658; // 0x108009a
field public static final int btn_default = 17301508; // 0x1080004
field public static final int btn_default_small = 17301509; // 0x1080005
@@ -5289,6 +5291,7 @@
method public java.lang.String getGroup();
method public android.graphics.drawable.Icon getLargeIcon();
method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
+ method public java.lang.CharSequence getSettingsText();
method public java.lang.String getShortcutId();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
@@ -5334,6 +5337,8 @@
field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+ field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+ field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
field public static final java.lang.String EXTRA_PEOPLE = "android.people";
field public static final java.lang.String EXTRA_PICTURE = "android.picture";
field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
@@ -5521,6 +5526,7 @@
method public android.app.Notification.Builder setProgress(int, int, boolean);
method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+ method public android.app.Notification.Builder setSettingsText(java.lang.CharSequence);
method public android.app.Notification.Builder setShortcutId(java.lang.String);
method public android.app.Notification.Builder setShowWhen(boolean);
method public android.app.Notification.Builder setSmallIcon(int);
@@ -11550,6 +11556,7 @@
field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+ field public static final int PROTECTION_FLAG_RUNTIME_ONLY = 8192; // 0x2000
field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
@@ -33581,6 +33588,14 @@
method public void open();
}
+ public final class ConfigUpdate {
+ field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
+ field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
+ field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
+ field public static final java.lang.String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
+ field public static final java.lang.String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES";
+ }
+
public abstract class CountDownTimer {
ctor public CountDownTimer(long, long);
method public final synchronized void cancel();
@@ -40358,7 +40373,6 @@
method public int getUser();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
- field public static final java.lang.String KEY_CHANNEL_ID = "key_channel_id";
field public static final java.lang.String KEY_PEOPLE = "key_people";
field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
}
@@ -40413,14 +40427,10 @@
ctor public NotificationAssistantService();
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
- method public void createNotificationChannel(java.lang.String, android.app.NotificationChannel);
- method public void deleteNotificationChannel(java.lang.String, java.lang.String);
- method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public final void unsnoozeNotification(java.lang.String);
- method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
@@ -40618,6 +40628,36 @@
}
+package android.service.resolver {
+
+ public abstract class ResolverRankerService extends android.app.Service {
+ ctor public ResolverRankerService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public void onPredictSharingProbabilities(java.util.List<android.service.resolver.ResolverTarget>);
+ method public void onTrainRankingModel(java.util.List<android.service.resolver.ResolverTarget>, int);
+ field public static final java.lang.String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService";
+ }
+
+ public final class ResolverTarget implements android.os.Parcelable {
+ ctor public ResolverTarget();
+ method public int describeContents();
+ method public float getChooserScore();
+ method public float getLaunchScore();
+ method public float getRecencyScore();
+ method public float getSelectProbability();
+ method public float getTimeSpentScore();
+ method public void setChooserScore(float);
+ method public void setLaunchScore(float);
+ method public void setRecencyScore(float);
+ method public void setSelectProbability(float);
+ method public void setTimeSpentScore(float);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.resolver.ResolverTarget> CREATOR;
+ }
+
+}
+
package android.service.restrictions {
public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver {
diff --git a/api/test-current.txt b/api/test-current.txt
index 8ea1dde..38897c8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1609,6 +1609,7 @@
field public static final int alert_light_frame = 17301505; // 0x1080001
field public static final int arrow_down_float = 17301506; // 0x1080002
field public static final int arrow_up_float = 17301507; // 0x1080003
+ field public static final int autofilled_highlight = 17301684; // 0x10800b4
field public static final int bottom_bar = 17301658; // 0x108009a
field public static final int btn_default = 17301508; // 0x1080004
field public static final int btn_default_small = 17301509; // 0x1080005
@@ -5128,6 +5129,7 @@
method public java.lang.String getChannel();
method public java.lang.String getGroup();
method public android.graphics.drawable.Icon getLargeIcon();
+ method public java.lang.CharSequence getSettingsText();
method public java.lang.String getShortcutId();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
@@ -5172,6 +5174,8 @@
field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+ field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+ field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
field public static final java.lang.String EXTRA_PEOPLE = "android.people";
field public static final java.lang.String EXTRA_PICTURE = "android.picture";
field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
@@ -5357,6 +5361,7 @@
method public android.app.Notification.Builder setProgress(int, int, boolean);
method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+ method public android.app.Notification.Builder setSettingsText(java.lang.CharSequence);
method public android.app.Notification.Builder setShortcutId(java.lang.String);
method public android.app.Notification.Builder setShowWhen(boolean);
method public android.app.Notification.Builder setSmallIcon(int);
@@ -10842,6 +10847,7 @@
field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+ field public static final int PROTECTION_FLAG_RUNTIME_ONLY = 8192; // 0x2000
field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
@@ -37454,7 +37460,6 @@
method public int getUser();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
- field public static final java.lang.String KEY_CHANNEL_ID = "key_channel_id";
field public static final java.lang.String KEY_PEOPLE = "key_people";
field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
}
@@ -37509,14 +37514,10 @@
ctor public NotificationAssistantService();
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
- method public void createNotificationChannel(java.lang.String, android.app.NotificationChannel);
- method public void deleteNotificationChannel(java.lang.String, java.lang.String);
- method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public final void unsnoozeNotification(java.lang.String);
- method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
@@ -46039,6 +46040,7 @@
method public void setAnimation(android.view.animation.Animation);
method public void setAutofillHints(java.lang.String...);
method public void setAutofillMode(int);
+ method public void setAutofilled(boolean);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
@@ -47310,6 +47312,7 @@
field public static final deprecated int MEMORY_TYPE_HARDWARE = 1; // 0x1
field public static final deprecated int MEMORY_TYPE_NORMAL = 0; // 0x0
field public static final deprecated int MEMORY_TYPE_PUSH_BUFFERS = 3; // 0x3
+ field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
field public static final int ROTATION_ANIMATION_CHANGED = 4096; // 0x1000
field public static final int ROTATION_ANIMATION_CROSSFADE = 1; // 0x1
field public static final int ROTATION_ANIMATION_JUMPCUT = 2; // 0x2
@@ -47370,6 +47373,7 @@
field public java.lang.String packageName;
field public int preferredDisplayModeId;
field public deprecated float preferredRefreshRate;
+ field public int privateFlags;
field public int rotationAnimation;
field public float screenBrightness;
field public int screenOrientation;
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index db17b28..ce114fd 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -101,8 +101,10 @@
doCompress = true;
} else if ("-nocompress".equals(arg)) {
doCompress = false;
- } else if ("-includekeyvalue".equals(arg)) {
+ } else if ("-keyvalue".equals(arg)) {
doKeyValue = true;
+ } else if ("-nokeyvalue".equals(arg)) {
+ doKeyValue = false;
} else {
Log.w(TAG, "Unknown backup flag " + arg);
continue;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c0505eb..af3bf2a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -724,6 +724,7 @@
public static final int FINISH_TASK_WITH_ACTIVITY = 2;
static final String FRAGMENTS_TAG = "android:fragments";
+ static final String AUTOFILL_RESET_NEEDED_TAG = "android:autofillResetNeeded";
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
@@ -1057,6 +1058,12 @@
* @see #onSaveInstanceState
*/
protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED_TAG, false);
+
+ if (mAutoFillResetNeeded) {
+ getSystemService(AutofillManager.class).onRestoreInstanceState(savedInstanceState);
+ }
+
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
@@ -1502,6 +1509,10 @@
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
+ if (mAutoFillResetNeeded) {
+ outState.putBoolean(AUTOFILL_RESET_NEEDED_TAG, mAutoFillResetNeeded);
+ getSystemService(AutofillManager.class).onSaveInstanceState(outState);
+ }
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
@@ -4166,25 +4177,14 @@
mTaskDescription.setPrimaryColor(colorPrimary);
}
}
-
- int colorBackground = a.getColor(
- com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0);
- if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) {
- mTaskDescription.setBackgroundColor(colorBackground);
+ // For dev-preview only.
+ if (mTaskDescription.getBackgroundColor() == 0) {
+ int colorBackground = a.getColor(
+ com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0);
+ if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) {
+ mTaskDescription.setBackgroundColor(colorBackground);
+ }
}
-
- final int statusBarColor = a.getColor(
- com.android.internal.R.styleable.ActivityTaskDescription_statusBarColor, 0);
- if (statusBarColor != 0) {
- mTaskDescription.setStatusBarColor(statusBarColor);
- }
-
- final int navigationBarColor = a.getColor(
- com.android.internal.R.styleable.ActivityTaskDescription_navigationBarColor, 0);
- if (navigationBarColor != 0) {
- mTaskDescription.setNavigationBarColor(navigationBarColor);
- }
-
a.recycle();
setTaskDescription(mTaskDescription);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index aede1bb..4004bd6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1145,8 +1145,6 @@
private String mIconFilename;
private int mColorPrimary;
private int mColorBackground;
- private int mStatusBarColor;
- private int mNavigationBarColor;
/**
* Creates the TaskDescription to the specified values.
@@ -1157,7 +1155,7 @@
* opaque.
*/
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
- this(label, icon, null, colorPrimary, 0, 0, 0);
+ this(label, icon, null, colorPrimary, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1170,7 +1168,7 @@
* @param icon An icon that represents the current state of this activity.
*/
public TaskDescription(String label, Bitmap icon) {
- this(label, icon, null, 0, 0, 0, 0);
+ this(label, icon, null, 0, 0);
}
/**
@@ -1179,26 +1177,24 @@
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
- this(label, null, null, 0, 0, 0, 0);
+ this(label, null, null, 0, 0);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
- this(null, null, null, 0, 0, 0, 0);
+ this(null, null, null, 0, 0);
}
/** @hide */
public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary,
- int colorBackground, int statusBarColor, int navigationBarColor) {
+ int colorBackground) {
mLabel = label;
mIcon = icon;
mIconFilename = iconFilename;
mColorPrimary = colorPrimary;
mColorBackground = colorBackground;
- mStatusBarColor = statusBarColor;
- mNavigationBarColor = navigationBarColor;
}
/**
@@ -1218,8 +1214,6 @@
mIconFilename = other.mIconFilename;
mColorPrimary = other.mColorPrimary;
mColorBackground = other.mColorBackground;
- mStatusBarColor = other.mStatusBarColor;
- mNavigationBarColor = other.mNavigationBarColor;
}
private TaskDescription(Parcel source) {
@@ -1259,20 +1253,6 @@
}
/**
- * @hide
- */
- public void setStatusBarColor(int statusBarColor) {
- mStatusBarColor = statusBarColor;
- }
-
- /**
- * @hide
- */
- public void setNavigationBarColor(int navigationBarColor) {
- mNavigationBarColor = navigationBarColor;
- }
-
- /**
* Sets the icon for this task description.
* @hide
*/
@@ -1345,20 +1325,6 @@
return mColorBackground;
}
- /**
- * @hide
- */
- public int getStatusBarColor() {
- return mStatusBarColor;
- }
-
- /**
- * @hide
- */
- public int getNavigationBarColor() {
- return mNavigationBarColor;
- }
-
/** @hide */
public void saveToXml(XmlSerializer out) throws IOException {
if (mLabel != null) {
@@ -1411,8 +1377,6 @@
}
dest.writeInt(mColorPrimary);
dest.writeInt(mColorBackground);
- dest.writeInt(mStatusBarColor);
- dest.writeInt(mNavigationBarColor);
if (mIconFilename == null) {
dest.writeInt(0);
} else {
@@ -1426,8 +1390,6 @@
mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
mColorPrimary = source.readInt();
mColorBackground = source.readInt();
- mStatusBarColor = source.readInt();
- mNavigationBarColor = source.readInt();
mIconFilename = source.readInt() > 0 ? source.readString() : null;
}
@@ -1445,9 +1407,7 @@
public String toString() {
return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
" IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary +
- " colorBackground: " + mColorBackground +
- " statusBarColor: " + mColorBackground +
- " navigationBarColor: " + mNavigationBarColor;
+ " colorBackground: " + mColorBackground;
}
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 43cad5b..61dacef 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -104,10 +104,6 @@
void applyEnqueuedAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
void applyAdjustmentsFromAssistant(in INotificationListener token, in List<Adjustment> adjustments);
- void createNotificationChannelFromAssistant(in INotificationListener token, String pkg, in NotificationChannel channel);
- void updateNotificationChannelFromAssistant(in INotificationListener token, String pkg, in NotificationChannel channel);
- void deleteNotificationChannelFromAssistant(in INotificationListener token, String pkg, String channelId);
- ParceledListSlice getNotificationChannelsFromAssistant(in INotificationListener token, String pkg);
void unsnoozeNotificationFromAssistant(in INotificationListener token, String key);
ComponentName getEffectsSuppressor();
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index 2bdfa99..88399e5 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.InstantAppResolveInfo;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -28,9 +29,12 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
import com.android.internal.os.SomeArgs;
+import java.util.Arrays;
import java.util.List;
/**
@@ -39,6 +43,9 @@
*/
@SystemApi
public abstract class InstantAppResolverService extends Service {
+ private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+ private static final String TAG = "PackageManager";
+
/** @hide */
public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
/** @hide */
@@ -132,11 +139,19 @@
@Deprecated
void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
InstantAppResolutionCallback callback) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Instant resolver; getInstantAppResolveInfo;"
+ + " prefix: " + Arrays.toString(digestPrefix));
+ }
onGetInstantAppResolveInfo(digestPrefix, token, callback);
}
@Deprecated
void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName,
InstantAppResolutionCallback callback) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Instant resolver; getInstantAppIntentFilter;"
+ + " prefix: " + Arrays.toString(digestPrefix));
+ }
onGetInstantAppIntentFilter(digestPrefix, token, callback);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 161dd25..6d7486b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -103,8 +103,7 @@
/**
* An activity that provides a user interface for adjusting notification preferences for its
- * containing application. Optional but recommended for apps that post
- * {@link android.app.Notification Notifications}.
+ * containing application.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
@@ -113,11 +112,25 @@
/**
* Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
* contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
- * what in app notifications settings should be shown.
+ * what settings should be shown in the target app.
*/
public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
/**
+ * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
+ * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
+ * that can be used to narrow down what settings should be shown in the target app.
+ */
+ public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
+
+ /**
+ * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
+ * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
+ * that can be used to narrow down what settings should be shown in the target app.
+ */
+ public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+
+ /**
* Use all default values (where applicable).
*/
public static final int DEFAULT_ALL = ~0;
@@ -1082,6 +1095,7 @@
private long mTimeout;
private String mShortcutId;
+ private CharSequence mSettingsText;
/**
* If this notification is being shown as a badge, always show as a number.
@@ -1851,6 +1865,10 @@
}
mBadgeIcon = parcel.readInt();
+
+ if (parcel.readInt() != 0) {
+ mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ }
}
@Override
@@ -1960,6 +1978,9 @@
that.mChannelId = this.mChannelId;
that.mTimeout = this.mTimeout;
+ that.mShortcutId = this.mShortcutId;
+ that.mBadgeIcon = this.mBadgeIcon;
+ that.mSettingsText = this.mSettingsText;
if (!heavy) {
that.lightenPayload(); // will clean out extras
@@ -2229,6 +2250,13 @@
}
parcel.writeInt(mBadgeIcon);
+
+ if (mSettingsText != null) {
+ parcel.writeInt(1);
+ TextUtils.writeToParcel(mSettingsText, parcel, flags);
+ } else {
+ parcel.writeInt(0);
+ }
}
/**
@@ -2458,6 +2486,14 @@
return mShortcutId;
}
+
+ /**
+ * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
+ */
+ public CharSequence getSettingsText() {
+ return mSettingsText;
+ }
+
/**
* The small icon representing this notification in the status bar and content view.
*
@@ -2887,6 +2923,24 @@
}
/**
+ * Provides text that will appear as a link to your application's settings.
+ *
+ * <p>This text does not appear within notification {@link Style templates} but may
+ * appear when the user uses an affordance to learn more about the notification.
+ * Additionally, this text will not appear unless you provide a valid link target by
+ * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
+ *
+ * <p>This text is meant to be concise description about what the user can customize
+ * when they click on this link. The recommended maximum length is 40 characters.
+ * @param text
+ * @return
+ */
+ public Builder setSettingsText(CharSequence text) {
+ mN.mSettingsText = safeCharSequence(text);
+ return this;
+ }
+
+ /**
* Set the remote input history.
*
* This should be set to the most recent inputs that have been sent
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index fe51633..545aef5 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -599,6 +599,10 @@
boolean mSanitized;
HtmlInfo mHtmlInfo;
+ // POJO used to override some autofill-related values when the node is parcelized.
+ // Not written to parcel.
+ AutofillOverlay mAutofillOverlay;
+
int mX;
int mY;
int mScrollX;
@@ -756,6 +760,7 @@
boolean writeSensitive = true;
int flags = mFlags & ~FLAGS_ALL_CONTROL;
+
if (mId != View.NO_ID) {
flags |= FLAGS_HAS_ID;
}
@@ -810,6 +815,13 @@
// Remove 'checked' from sanitized autofill request.
writtenFlags = flags & ~FLAGS_CHECKED;
}
+ if (mAutofillOverlay != null) {
+ if (mAutofillOverlay.focused) {
+ writtenFlags |= ViewNode.FLAGS_FOCUSED;
+ } else {
+ writtenFlags &= ~ViewNode.FLAGS_FOCUSED;
+ }
+ }
out.writeInt(writtenFlags);
if ((flags&FLAGS_HAS_ID) != 0) {
@@ -829,7 +841,14 @@
out.writeParcelable(mAutofillId, 0);
out.writeInt(mAutofillType);
out.writeStringArray(mAutofillHints);
- final AutofillValue sanitizedValue = writeSensitive ? mAutofillValue : null;
+ final AutofillValue sanitizedValue;
+ if (mAutofillOverlay != null && mAutofillOverlay.value != null) {
+ sanitizedValue = mAutofillOverlay.value;
+ } else if (writeSensitive) {
+ sanitizedValue = mAutofillValue;
+ } else {
+ sanitizedValue = null;
+ }
out.writeParcelable(sanitizedValue, 0);
out.writeStringArray(mAutofillOptions);
if (mHtmlInfo instanceof Parcelable) {
@@ -959,6 +978,11 @@
return mAutofillValue;
}
+ /** @hide **/
+ public void setAutofillOverlay(AutofillOverlay overlay) {
+ mAutofillOverlay = overlay;
+ }
+
/**
* Gets the options that can be used to autofill this structure.
*
@@ -1340,6 +1364,16 @@
}
}
+ /**
+ * POJO used to override some autofill-related values when the node is parcelized.
+ *
+ * @hide
+ */
+ static public class AutofillOverlay {
+ public boolean focused;
+ public AutofillValue value;
+ }
+
static class ViewNodeBuilder extends ViewStructure {
final AssistStructure mAssist;
final ViewNode mNode;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2dfb45f..940447c 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3114,6 +3114,7 @@
if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) == 0
+ && (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
PermissionInfo.PROTECTION_SIGNATURE) {
outError[0] = "<permission> protectionLevel specifies a non-ephemeral flag but is "
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 0703138..694e607 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -131,6 +131,13 @@
public static final int PROTECTION_FLAG_EPHEMERAL = 0x1000;
/**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>runtime</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ */
+ public static final int PROTECTION_FLAG_RUNTIME_ONLY = 0x2000;
+
+ /**
* Mask for {@link #protectionLevel}: the basic protection type.
*/
public static final int PROTECTION_MASK_BASE = 0xf;
@@ -250,6 +257,9 @@
if ((level&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0) {
protLevel += "|ephemeral";
}
+ if ((level&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
+ protLevel += "|runtime";
+ }
return protLevel;
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 2aa6af6..46ad3f0 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -67,6 +67,15 @@
*
* @hide
*/
+ public Key(String name, Class<T> type, long vendorId) {
+ mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
+ }
+
+ /**
+ * Visible for testing and vendor extensions only.
+ *
+ * @hide
+ */
public Key(String name, Class<T> type) {
mKey = new CameraMetadataNative.Key<T>(name, type);
}
@@ -99,6 +108,15 @@
}
/**
+ * Return vendor tag id.
+ *
+ * @hide
+ */
+ public long getVendorId() {
+ return mKey.getVendorId();
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -159,6 +177,7 @@
*/
public CameraCharacteristics(CameraMetadataNative properties) {
mProperties = CameraMetadataNative.move(properties);
+ setNativeInstance(mProperties);
}
/**
@@ -227,7 +246,7 @@
}
mKeys = Collections.unmodifiableList(
- getKeysStatic(getClass(), getKeyClass(), this, filterTags));
+ getKeys(getClass(), getKeyClass(), this, filterTags));
return mKeys;
}
@@ -320,7 +339,7 @@
"metadataClass must be a subclass of CameraMetadata");
}
- List<TKey> staticKeyList = CameraCharacteristics.<TKey>getKeysStatic(
+ List<TKey> staticKeyList = getKeys(
metadataClass, keyClass, /*instance*/null, filterTags);
return Collections.unmodifiableList(staticKeyList);
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index e289627..8c8c49f 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -52,6 +52,7 @@
private static final String TAG = "CameraMetadataAb";
private static final boolean DEBUG = false;
+ private CameraMetadataNative mNativeInstance = null;
/**
* Set a camera metadata field to a value. The field definitions can be
@@ -89,6 +90,13 @@
/**
* @hide
*/
+ protected void setNativeInstance(CameraMetadataNative nativeInstance) {
+ mNativeInstance = nativeInstance;
+ }
+
+ /**
+ * @hide
+ */
protected abstract Class<TKey> getKeyClass();
/**
@@ -108,7 +116,7 @@
public List<TKey> getKeys() {
Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
return Collections.unmodifiableList(
- getKeysStatic(thisClass, getKeyClass(), this, /*filterTags*/null));
+ getKeys(thisClass, getKeyClass(), this, /*filterTags*/null));
}
/**
@@ -126,7 +134,7 @@
* </p>
*/
/*package*/ @SuppressWarnings("unchecked")
- static <TKey> ArrayList<TKey> getKeysStatic(
+ <TKey> ArrayList<TKey> getKeys(
Class<?> type, Class<TKey> keyClass,
CameraMetadata<TKey> instance,
int[] filterTags) {
@@ -173,23 +181,31 @@
}
}
- ArrayList<TKey> vendorKeys = CameraMetadataNative.getAllVendorKeys(keyClass);
+ if (null == mNativeInstance) {
+ return keyList;
+ }
+
+ ArrayList<TKey> vendorKeys = mNativeInstance.getAllVendorKeys(keyClass);
if (vendorKeys != null) {
for (TKey k : vendorKeys) {
String keyName;
+ long vendorId;
if (k instanceof CaptureRequest.Key<?>) {
keyName = ((CaptureRequest.Key<?>) k).getName();
+ vendorId = ((CaptureRequest.Key<?>) k).getVendorId();
} else if (k instanceof CaptureResult.Key<?>) {
keyName = ((CaptureResult.Key<?>) k).getName();
+ vendorId = ((CaptureResult.Key<?>) k).getVendorId();
} else if (k instanceof CameraCharacteristics.Key<?>) {
keyName = ((CameraCharacteristics.Key<?>) k).getName();
+ vendorId = ((CameraCharacteristics.Key<?>) k).getVendorId();
} else {
continue;
}
if (filterTags == null || Arrays.binarySearch(filterTags,
- CameraMetadataNative.getTag(keyName)) >= 0) {
+ CameraMetadataNative.getTag(keyName, vendorId)) >= 0) {
keyList.add(k);
}
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 12b46c1..1cf8f03 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -101,6 +101,15 @@
*
* @hide
*/
+ public Key(String name, Class<T> type, long vendorId) {
+ mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
+ }
+
+ /**
+ * Visible for testing and vendor extensions only.
+ *
+ * @hide
+ */
public Key(String name, Class<T> type) {
mKey = new CameraMetadataNative.Key<T>(name, type);
}
@@ -133,6 +142,15 @@
}
/**
+ * Return vendor tag id.
+ *
+ * @hide
+ */
+ public long getVendorId() {
+ return mKey.getVendorId();
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -199,6 +217,7 @@
*/
private CaptureRequest() {
mSettings = new CameraMetadataNative();
+ setNativeInstance(mSettings);
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = false;
mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
@@ -212,6 +231,7 @@
@SuppressWarnings("unchecked")
private CaptureRequest(CaptureRequest source) {
mSettings = new CameraMetadataNative(source.mSettings);
+ setNativeInstance(mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
mIsReprocess = source.mIsReprocess;
mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList;
@@ -242,6 +262,7 @@
private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
int reprocessableSessionId) {
mSettings = CameraMetadataNative.move(settings);
+ setNativeInstance(mSettings);
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = isReprocess;
if (isReprocess) {
@@ -441,6 +462,7 @@
*/
private void readFromParcel(Parcel in) {
mSettings.readFromParcel(in);
+ setNativeInstance(mSettings);
mSurfaceSet.clear();
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 3f8b57a..419e3e2 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -78,6 +78,15 @@
*
* @hide
*/
+ public Key(String name, Class<T> type, long vendorId) {
+ mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
+ }
+
+ /**
+ * Visible for testing and vendor extensions only.
+ *
+ * @hide
+ */
public Key(String name, Class<T> type) {
mKey = new CameraMetadataNative.Key<T>(name, type);
}
@@ -110,6 +119,15 @@
}
/**
+ * Return vendor tag id.
+ *
+ * @hide
+ */
+ public long getVendorId() {
+ return mKey.getVendorId();
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -186,6 +204,7 @@
if (mResults.isEmpty()) {
throw new AssertionError("Results must not be empty");
}
+ setNativeInstance(mResults);
mRequest = parent;
mSequenceId = extras.getRequestId();
mFrameNumber = extras.getFrameNumber();
@@ -215,6 +234,7 @@
throw new AssertionError("Results must not be empty");
}
+ setNativeInstance(mResults);
mRequest = null;
mSequenceId = sequenceId;
mFrameNumber = -1;
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 4d92ab1cc..ebe2fa1 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -79,10 +79,28 @@
public static class Key<T> {
private boolean mHasTag;
private int mTag;
+ private long mVendorId = Long.MAX_VALUE;
private final Class<T> mType;
private final TypeReference<T> mTypeReference;
private final String mName;
private final int mHash;
+
+ /**
+ * @hide
+ */
+ public Key(String name, Class<T> type, long vendorId) {
+ if (name == null) {
+ throw new NullPointerException("Key needs a valid name");
+ } else if (type == null) {
+ throw new NullPointerException("Type needs to be non-null");
+ }
+ mName = name;
+ mType = type;
+ mVendorId = vendorId;
+ mTypeReference = TypeReference.createSpecializedTypeReference(type);
+ mHash = mName.hashCode() ^ mTypeReference.hashCode();
+ }
+
/**
* Visible for testing only.
*
@@ -194,7 +212,7 @@
*/
public final int getTag() {
if (!mHasTag) {
- mTag = CameraMetadataNative.getTag(mName);
+ mTag = CameraMetadataNative.getTag(mName, mVendorId);
mHasTag = true;
}
return mTag;
@@ -212,6 +230,15 @@
}
/**
+ * Get the vendor tag provider id.
+ *
+ * @hide
+ */
+ public final long getVendorId() {
+ return mVendorId;
+ }
+
+ /**
* Get the type reference backing the type {@code T} for this key.
*
* <p>The distinction is only important if {@code T} is a generic, e.g.
@@ -463,13 +490,14 @@
}
private <T> T getBase(Key<T> key) {
- int tag = key.getTag();
+ int tag = nativeGetTagFromKeyLocal(key.getName());
byte[] values = readValues(tag);
if (values == null) {
return null;
}
- Marshaler<T> marshaler = getMarshalerForKey(key);
+ int nativeType = nativeGetTypeFromTagLocal(tag);
+ Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
return marshaler.unmarshal(buffer);
}
@@ -947,15 +975,15 @@
}
private <T> void setBase(Key<T> key, T value) {
- int tag = key.getTag();
-
+ int tag = nativeGetTagFromKeyLocal(key.getName());
if (value == null) {
// Erase the entry
writeValues(tag, /*src*/null);
return;
} // else update the entry to a new value
- Marshaler<T> marshaler = getMarshalerForKey(key);
+ int nativeType = nativeGetTypeFromTagLocal(tag);
+ Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
int size = marshaler.calculateMarshalSize(value);
// TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
@@ -1092,10 +1120,14 @@
private native synchronized void nativeWriteValues(int tag, byte[] src);
private native synchronized void nativeDump() throws IOException; // dump to ALOGD
- private static native ArrayList nativeGetAllVendorKeys(Class keyClass);
- private static native int nativeGetTagFromKey(String keyName)
+ private native synchronized ArrayList nativeGetAllVendorKeys(Class keyClass);
+ private native synchronized int nativeGetTagFromKeyLocal(String keyName)
throws IllegalArgumentException;
- private static native int nativeGetTypeFromTag(int tag)
+ private native synchronized int nativeGetTypeFromTagLocal(int tag)
+ throws IllegalArgumentException;
+ private static native int nativeGetTagFromKey(String keyName, long vendorId)
+ throws IllegalArgumentException;
+ private static native int nativeGetTypeFromTag(int tag, long vendorId)
throws IllegalArgumentException;
/**
@@ -1133,7 +1165,7 @@
*
* @hide
*/
- public static <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
+ public <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
if (keyClass == null) {
throw new NullPointerException();
}
@@ -1149,19 +1181,32 @@
* @hide
*/
public static int getTag(String key) {
- return nativeGetTagFromKey(key);
+ return nativeGetTagFromKey(key, Long.MAX_VALUE);
+ }
+
+ /**
+ * Convert a key string into the equivalent native tag.
+ *
+ * @throws IllegalArgumentException if the key was not recognized
+ * @throws NullPointerException if the key was null
+ *
+ * @hide
+ */
+ public static int getTag(String key, long vendorId) {
+ return nativeGetTagFromKey(key, vendorId);
}
/**
* Get the underlying native type for a tag.
*
* @param tag An integer tag, see e.g. {@link #getTag}
+ * @param vendorId A vendor tag provider id
* @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
*
* @hide
*/
- public static int getNativeType(int tag) {
- return nativeGetTypeFromTag(tag);
+ public static int getNativeType(int tag, long vendorId) {
+ return nativeGetTypeFromTag(tag, vendorId);
}
/**
@@ -1226,9 +1271,9 @@
* @throws UnsupportedOperationException
* if the native/managed type combination for {@code key} is not supported
*/
- private static <T> Marshaler<T> getMarshalerForKey(Key<T> key) {
+ private static <T> Marshaler<T> getMarshalerForKey(Key<T> key, int nativeType) {
return MarshalRegistry.getMarshaler(key.getTypeReference(),
- getNativeType(key.getTag()));
+ nativeType);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
diff --git a/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java b/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java
new file mode 100644
index 0000000..1f92f6d
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * A class for describing the vendor tag cache declared by a camera HAL module.
+ * Generally only used by the native side of
+ * android.hardware.camera2.impl.CameraMetadataNative
+ *
+ * @hide
+ */
+public final class VendorTagDescriptorCache implements Parcelable {
+
+ private VendorTagDescriptorCache(Parcel source) {
+ }
+
+ public static final Parcelable.Creator<VendorTagDescriptorCache> CREATOR =
+ new Parcelable.Creator<VendorTagDescriptorCache>() {
+ @Override
+ public VendorTagDescriptorCache createFromParcel(Parcel source) {
+ try {
+ VendorTagDescriptorCache vendorDescriptorCache = new VendorTagDescriptorCache(source);
+ return vendorDescriptorCache;
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating VendorTagDescriptorCache from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public VendorTagDescriptorCache[] newArray(int size) {
+ return new VendorTagDescriptorCache[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (dest == null) {
+ throw new IllegalArgumentException("dest must not be null");
+ }
+ }
+
+ private static final String TAG = "VendorTagDescriptorCache";
+}
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
new file mode 100644
index 0000000..304ee29
--- /dev/null
+++ b/core/java/android/os/ConfigUpdate.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.SystemApi;
+
+/**
+ * Intents used to provide unbundled updates of system data.
+ * All require the UPDATE_CONFIG permission.
+ *
+ * @see com.android.server.updates
+ * @hide
+ */
+@SystemApi
+public final class ConfigUpdate {
+
+ /**
+ * Update system wide certificate pins for TLS connections.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
+
+ /**
+ * Update system wide Intent firewall.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UPDATE_INTENT_FIREWALL
+ = "android.intent.action.UPDATE_INTENT_FIREWALL";
+
+ /**
+ * Update list of permium SMS short codes.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UPDATE_SMS_SHORT_CODES
+ = "android.intent.action.UPDATE_SMS_SHORT_CODES";
+
+ /**
+ * Update list of carrier provisioning URLs.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UPDATE_CARRIER_PROVISIONING_URLS
+ = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
+
+ /**
+ * Update set of trusted logs used for Certificate Transparency support for TLS connections.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UPDATE_CT_LOGS
+ = "android.intent.action.UPDATE_CT_LOGS";
+
+ private ConfigUpdate() {
+ }
+}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 9f8a621..ef14095 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -204,11 +204,12 @@
* to notify the result of the request.
*
* @param structure {@link Activity}'s view structure.
- * @param data bundle containing data passed by the service on previous calls to fill.
- * This bundle allows your service to keep state between fill and save requests
- * as well as when filling different sections of the UI as the system will try to
- * aggressively unbind from the service to conserve resources. See {@link
- * FillResponse} Javadoc for examples of multiple-sections requests.
+ * @param data bundle containing data passed by the service in a last call to
+ * {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your
+ * service to keep state between fill and save requests as well as when filling different
+ * sections of the UI as the system will try to aggressively unbind from the service to
+ * conserve resources.
+ * See {@link FillResponse} for examples of multiple-sections requests.
* @param flags either {@code 0} or {@link AutofillManager#FLAG_MANUAL_REQUEST}.
* @param cancellationSignal signal for observing cancellation requests. The system will use
* this to notify you that the fill result is no longer needed and you should stop
@@ -242,11 +243,12 @@
* to notify the result of the request.
*
* @param structure {@link Activity}'s view structure.
- * @param data bundle containing data passed by the service on previous calls to fill.
- * This bundle allows your service to keep state between fill and save requests
- * as well as when filling different sections of the UI as the system will try to
- * aggressively unbind from the service to conserve resources. See {@link
- * FillResponse} Javadoc for examples of multiple-sections requests.
+ * @param data bundle containing data passed by the service in a last call to
+ * {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your
+ * service to keep state between fill and save requests as well as when filling different
+ * sections of the UI as the system will try to aggressively unbind from the service to
+ * conserve resources.
+ * See {@link FillResponse} for examples of multiple-sections requests.
* @param callback object used to notify the result of the request.
*/
public abstract void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 717312b..3117f98 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -297,6 +297,9 @@
* android.os.CancellationSignal, FillCallback)} and {@link AutofillService#onSaveRequest(
* android.app.assist.AssistStructure, Bundle, SaveCallback)}.
*
+ * <p>If this method is called on multiple {@link FillResponse} objects for the same
+ * activity, just the latest bundle is passed back to the service.
+ *
* @param extras The response extras.
* @return This builder.
*/
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index e39d53f..137cf57 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -36,10 +36,6 @@
private final int mUser;
/**
- * Data type: {@code String}. See {@link NotificationChannel#getId()}.
- */
- public static final String KEY_CHANNEL_ID = "key_channel_id";
- /**
* Data type: ArrayList of {@code String}, where each is a representation of a
* {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
* See {@link android.app.Notification.Builder#addPerson(String)}.
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 46609df..6ec9d69 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -138,69 +138,6 @@
}
}
- /**
- * Creates a notification channel that notifications can be posted to for a given package.
- *
- * @param pkg The package to create a channel for.
- * @param channel the channel to attempt to create.
- */
- public void createNotificationChannel(@NonNull String pkg,
- @NonNull NotificationChannel channel) {
- if (!isBound()) return;
- try {
- getNotificationInterface().createNotificationChannelFromAssistant(
- mWrapper, pkg, channel);
- } catch (RemoteException e) {
- Log.v(TAG, "Unable to contact notification manager", e);
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Updates a notification channel for a given package.
- *
- * @param pkg The package to the channel belongs to.
- * @param channel the channel to attempt to update.
- */
- public void updateNotificationChannel(@NonNull String pkg,
- @NonNull NotificationChannel channel) {
- if (!isBound()) return;
- try {
- getNotificationInterface().updateNotificationChannelFromAssistant(
- mWrapper, pkg, channel);
- } catch (RemoteException e) {
- Log.v(TAG, "Unable to contact notification manager", e);
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Returns all notification channels belonging to the given package.
- */
- public List<NotificationChannel> getNotificationChannels(@NonNull String pkg) {
- if (!isBound()) return null;
- try {
- return getNotificationInterface().getNotificationChannelsFromAssistant(
- mWrapper, pkg).getList();
- } catch (RemoteException e) {
- Log.v(TAG, "Unable to contact notification manager", e);
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Deletes the given notification channel.
- */
- public void deleteNotificationChannel(@NonNull String pkg, @NonNull String channelId) {
- if (!isBound()) return;
- try {
- getNotificationInterface().deleteNotificationChannelFromAssistant(
- mWrapper, pkg, channelId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
@Override
diff --git a/core/java/android/service/resolver/IResolverRankerResult.aidl b/core/java/android/service/resolver/IResolverRankerResult.aidl
new file mode 100644
index 0000000..bda3154
--- /dev/null
+++ b/core/java/android/service/resolver/IResolverRankerResult.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resolver;
+
+import android.service.resolver.ResolverTarget;
+
+/**
+ * @hide
+ */
+oneway interface IResolverRankerResult
+{
+ void sendResult(in List<ResolverTarget> results);
+}
\ No newline at end of file
diff --git a/core/java/android/service/resolver/IResolverRankerService.aidl b/core/java/android/service/resolver/IResolverRankerService.aidl
new file mode 100644
index 0000000..f0d747d
--- /dev/null
+++ b/core/java/android/service/resolver/IResolverRankerService.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resolver;
+
+import android.service.resolver.IResolverRankerResult;
+import android.service.resolver.ResolverTarget;
+
+/**
+ * @hide
+ */
+oneway interface IResolverRankerService
+{
+ void predict(in List<ResolverTarget> targets, IResolverRankerResult result);
+ void train(in List<ResolverTarget> targets, int selectedPosition);
+}
\ No newline at end of file
diff --git a/core/java/android/service/resolver/ResolverRankerService.java b/core/java/android/service/resolver/ResolverRankerService.java
new file mode 100644
index 0000000..0506747
--- /dev/null
+++ b/core/java/android/service/resolver/ResolverRankerService.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resolver;
+
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteException;
+import android.service.resolver.ResolverTarget;
+import android.util.Log;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A service to rank apps according to usage stats of apps, when the system is resolving targets for
+ * an Intent.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE} permission, and include an
+ * intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
+ * <pre>
+ * <service android:name=".MyResolverRankerService"
+ * android:exported="true"
+ * android:priority="100"
+ * android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.resolver.ResolverRankerService" />
+ * </intent-filter>
+ * </service>
+ * </pre>
+ * @hide
+ */
+@SystemApi
+public abstract class ResolverRankerService extends Service {
+
+ private static final String TAG = "ResolverRankerService";
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * The Intent action that a service must respond to. Add it to the intent filter of the service
+ * in its manifest.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService";
+
+ /**
+ * The permission that a service must require to ensure that only Android system can bind to it.
+ * If this permission is not enforced in the AndroidManifest of the service, the system will
+ * skip that service.
+ */
+ public static final String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
+
+ private ResolverRankerServiceWrapper mWrapper = null;
+
+ /**
+ * Called by the system to retrieve a list of probabilities to rank apps/options. To implement
+ * it, set selectProbability of each input {@link ResolverTarget}. The higher the
+ * selectProbability is, the more likely the {@link ResolverTarget} will be selected by the
+ * user. Override this function to provide prediction results.
+ *
+ * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked.
+ *
+ * @throws Exception when the prediction task fails.
+ */
+ public void onPredictSharingProbabilities(final List<ResolverTarget> targets) {}
+
+ /**
+ * Called by the system to train/update a ranking service, after the user makes a selection from
+ * the ranked list of apps. Override this function to enable model updates.
+ *
+ * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked.
+ * @param selectedPosition the position of the selected app in the list.
+ *
+ * @throws Exception when the training task fails.
+ */
+ public void onTrainRankingModel(
+ final List<ResolverTarget> targets, final int selectedPosition) {}
+
+ private static final String HANDLER_THREAD_NAME = "RESOLVER_RANKER_SERVICE";
+ private volatile Handler mHandler;
+ private HandlerThread mHandlerThread;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (DEBUG) Log.d(TAG, "onBind " + intent);
+ if (!SERVICE_INTERFACE.equals(intent.getAction())) {
+ if (DEBUG) Log.d(TAG, "bad intent action " + intent.getAction() + "; returning null");
+ return null;
+ }
+ if (mHandlerThread == null) {
+ mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+ if (mWrapper == null) {
+ mWrapper = new ResolverRankerServiceWrapper();
+ }
+ return mWrapper;
+ }
+
+ @Override
+ public void onDestroy() {
+ mHandler = null;
+ if (mHandlerThread != null) {
+ mHandlerThread.quitSafely();
+ }
+ super.onDestroy();
+ }
+
+ private static void sendResult(List<ResolverTarget> targets, IResolverRankerResult result) {
+ try {
+ result.sendResult(targets);
+ } catch (Exception e) {
+ Log.e(TAG, "failed to send results: " + e);
+ }
+ }
+
+ private class ResolverRankerServiceWrapper extends IResolverRankerService.Stub {
+
+ @Override
+ public void predict(final List<ResolverTarget> targets, final IResolverRankerResult result)
+ throws RemoteException {
+ Runnable predictRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "predict calls onPredictSharingProbabilities.");
+ }
+ onPredictSharingProbabilities(targets);
+ sendResult(targets, result);
+ } catch (Exception e) {
+ Log.e(TAG, "onPredictSharingProbabilities failed; send null results: " + e);
+ sendResult(null, result);
+ }
+ }
+ };
+ final Handler h = mHandler;
+ if (h != null) {
+ h.post(predictRunnable);
+ }
+ }
+
+ @Override
+ public void train(final List<ResolverTarget> targets, final int selectedPosition)
+ throws RemoteException {
+ Runnable trainRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "train calls onTranRankingModel");
+ }
+ onTrainRankingModel(targets, selectedPosition);
+ } catch (Exception e) {
+ Log.e(TAG, "onTrainRankingModel failed; skip train: " + e);
+ }
+ }
+ };
+ final Handler h = mHandler;
+ if (h != null) {
+ h.post(trainRunnable);
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/resolver/ResolverTarget.aidl b/core/java/android/service/resolver/ResolverTarget.aidl
new file mode 100644
index 0000000..6cab2d4
--- /dev/null
+++ b/core/java/android/service/resolver/ResolverTarget.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resolver;
+
+/**
+ * @hide
+ */
+parcelable ResolverTarget;
diff --git a/core/java/android/service/resolver/ResolverTarget.java b/core/java/android/service/resolver/ResolverTarget.java
new file mode 100644
index 0000000..fb3e2d7
--- /dev/null
+++ b/core/java/android/service/resolver/ResolverTarget.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resolver;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/**
+ * A ResolverTarget contains features by which an app or option will be ranked, in
+ * {@link ResolverRankerService}.
+ * @hide
+ */
+@SystemApi
+public final class ResolverTarget implements Parcelable {
+ private static final String TAG = "ResolverTarget";
+
+ /**
+ * a float score for recency of last use.
+ */
+ private float mRecencyScore;
+
+ /**
+ * a float score for total time spent.
+ */
+ private float mTimeSpentScore;
+
+ /**
+ * a float score for number of launches.
+ */
+ private float mLaunchScore;
+
+ /**
+ * a float score for number of selected.
+ */
+ private float mChooserScore;
+
+ /**
+ * a float score for the probability to be selected.
+ */
+ private float mSelectProbability;
+
+ // constructor for the class.
+ public ResolverTarget() {}
+
+ ResolverTarget(Parcel in) {
+ mRecencyScore = in.readFloat();
+ mTimeSpentScore = in.readFloat();
+ mLaunchScore = in.readFloat();
+ mChooserScore = in.readFloat();
+ mSelectProbability = in.readFloat();
+ }
+
+ /**
+ * Gets the score for how recently the target was used in the foreground.
+ *
+ * @return a float score whose range is [0, 1]. The higher the score is, the more recently the
+ * target was used.
+ */
+ public float getRecencyScore() {
+ return mRecencyScore;
+ }
+
+ /**
+ * Sets the score for how recently the target was used in the foreground.
+ *
+ * @param recencyScore a float score whose range is [0, 1]. The higher the score is, the more
+ * recently the target was used.
+ */
+ public void setRecencyScore(float recencyScore) {
+ this.mRecencyScore = recencyScore;
+ }
+
+ /**
+ * Gets the score for how long the target has been used in the foreground.
+ *
+ * @return a float score whose range is [0, 1]. The higher the score is, the longer the target
+ * has been used for.
+ */
+ public float getTimeSpentScore() {
+ return mTimeSpentScore;
+ }
+
+ /**
+ * Sets the score for how long the target has been used in the foreground.
+ *
+ * @param timeSpentScore a float score whose range is [0, 1]. The higher the score is, the
+ * longer the target has been used for.
+ */
+ public void setTimeSpentScore(float timeSpentScore) {
+ this.mTimeSpentScore = timeSpentScore;
+ }
+
+ /**
+ * Gets the score for how many times the target has been launched to the foreground.
+ *
+ * @return a float score whose range is [0, 1]. The higher the score is, the more times the
+ * target has been launched.
+ */
+ public float getLaunchScore() {
+ return mLaunchScore;
+ }
+
+ /**
+ * Sets the score for how many times the target has been launched to the foreground.
+ *
+ * @param launchScore a float score whose range is [0, 1]. The higher the score is, the more
+ * times the target has been launched.
+ */
+ public void setLaunchScore(float launchScore) {
+ this.mLaunchScore = launchScore;
+ }
+
+ /**
+ * Gets the score for how many times the target has been selected by the user to share the same
+ * types of content.
+ *
+ * @return a float score whose range is [0, 1]. The higher the score is, the
+ * more times the target has been selected by the user to share the same types of content for.
+ */
+ public float getChooserScore() {
+ return mChooserScore;
+ }
+
+ /**
+ * Sets the score for how many times the target has been selected by the user to share the same
+ * types of content.
+ *
+ * @param chooserScore a float score whose range is [0, 1]. The higher the score is, the more
+ * times the target has been selected by the user to share the same types
+ * of content for.
+ */
+ public void setChooserScore(float chooserScore) {
+ this.mChooserScore = chooserScore;
+ }
+
+ /**
+ * Gets the probability of how likely this target will be selected by the user.
+ *
+ * @return a float score whose range is [0, 1]. The higher the score is, the more likely the
+ * user is going to select this target.
+ */
+ public float getSelectProbability() {
+ return mSelectProbability;
+ }
+
+ /**
+ * Sets the probability for how like this target will be selected by the user.
+ *
+ * @param selectProbability a float score whose range is [0, 1]. The higher the score is, the
+ * more likely tht user is going to select this target.
+ */
+ public void setSelectProbability(float selectProbability) {
+ this.mSelectProbability = selectProbability;
+ }
+
+ // serialize the class to a string.
+ @Override
+ public String toString() {
+ return "ResolverTarget{"
+ + mRecencyScore + ", "
+ + mTimeSpentScore + ", "
+ + mLaunchScore + ", "
+ + mChooserScore + ", "
+ + mSelectProbability + "}";
+ }
+
+ // describes the kinds of special objects contained in this Parcelable instance's marshaled
+ // representation.
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ // flattens this object in to a Parcel.
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeFloat(mRecencyScore);
+ dest.writeFloat(mTimeSpentScore);
+ dest.writeFloat(mLaunchScore);
+ dest.writeFloat(mChooserScore);
+ dest.writeFloat(mSelectProbability);
+ }
+
+ // creator definition for the class.
+ public static final Creator<ResolverTarget> CREATOR
+ = new Creator<ResolverTarget>() {
+ @Override
+ public ResolverTarget createFromParcel(Parcel source) {
+ return new ResolverTarget(source);
+ }
+
+ @Override
+ public ResolverTarget[] newArray(int size) {
+ return new ResolverTarget[size];
+ }
+ };
+}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 5d8f336..829b2b7 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -494,7 +494,8 @@
* On TV remotes, switches to viewing live TV. */
public static final int KEYCODE_TV = 170;
/** Key code constant: Window key.
- * On TV remotes, toggles picture-in-picture mode or other windowing functions. */
+ * On TV remotes, toggles picture-in-picture mode or other windowing functions.
+ * On Android Wear devices, triggers a display offset. */
public static final int KEYCODE_WINDOW = 171;
/** Key code constant: Guide key.
* On TV remotes, shows a programming guide. */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4ffcd95..b4100123 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2750,7 +2750,7 @@
* 1 PFLAG3_SCROLL_INDICATOR_END
* 1 PFLAG3_ASSIST_BLOCKED
* 1 PFLAG3_CLUSTER
- * x * NO LONGER NEEDED, SHOULD BE REUSED *
+ * 1 PFLAG3_IS_AUTOFILLED
* 1 PFLAG3_FINGER_DOWN
* 1 PFLAG3_FOCUSED_BY_DEFAULT
* 11 PFLAG3_AUTO_FILL_MODE_MASK
@@ -2961,6 +2961,14 @@
private static final int PFLAG3_CLUSTER = 0x8000;
/**
+ * Flag indicating that the view is autofilled
+ *
+ * @see #isAutofilled()
+ * @see #setAutofilled(boolean)
+ */
+ private static final int PFLAG3_IS_AUTOFILLED = 0x10000;
+
+ /**
* Indicates that the user is currently touching the screen.
* Currently used for the tooltip positioning only.
*/
@@ -7423,6 +7431,9 @@
AccessibilityNodeInfo info = createAccessibilityNodeInfo();
structure.setChildCount(1);
ViewStructure root = structure.newChild(0);
+ if (forAutofill) {
+ setAutofillId(root);
+ }
populateVirtualStructure(root, provider, info, forAutofill);
info.recycle();
}
@@ -7440,10 +7451,9 @@
* <ol>
* <li>Calling the proper getter method on {@link AutofillValue} to fetch the actual value.
* <li>Passing the actual value to the equivalent setter in the view.
- * <ol>
+ * </ol>
*
* <p>For example, a text-field view would call:
- *
* <pre class="prettyprint">
* CharSequence text = value.getTextValue();
* if (text != null) {
@@ -7451,6 +7461,10 @@
* }
* </pre>
*
+ * <p>If the value is updated asyncronously the next call to
+ * {@link AutofillManager#notifyValueChanged(View)} must happen <u>after</u> the value was
+ * changed to the autofilled value. If not, the view will not be considered autofilled.
+ *
* @param value value to be autofilled.
*/
public void autofill(@SuppressWarnings("unused") AutofillValue value) {
@@ -7461,6 +7475,9 @@
*
* <p>See {@link #autofill(AutofillValue)} and
* {@link #onProvideAutofillVirtualStructure(ViewStructure, int)} for more info.
+ * <p>To indicate that a virtual view was autofilled
+ * <code>@android:drawable/autofilled_highlight</code> should be drawn over it until the data
+ * changes.
*
* @param values map of values to be autofilled, keyed by virtual child id.
*/
@@ -7491,6 +7508,13 @@
}
/**
+ * @hide
+ */
+ public boolean isAutofilled() {
+ return (mPrivateFlags3 & PFLAG3_IS_AUTOFILLED) != 0;
+ }
+
+ /**
* Gets the {@link View}'s current autofill value.
*
* <p>By default returns {@code null}, but views should override it (and
@@ -9131,6 +9155,24 @@
}
/**
+ * @hide
+ */
+ @TestApi
+ public void setAutofilled(boolean isAutofilled) {
+ boolean wasChanged = isAutofilled != isAutofilled();
+
+ if (wasChanged) {
+ if (isAutofilled) {
+ mPrivateFlags3 |= PFLAG3_IS_AUTOFILLED;
+ } else {
+ mPrivateFlags3 &= ~PFLAG3_IS_AUTOFILLED;
+ }
+
+ invalidate();
+ }
+ }
+
+ /**
* Set whether this view should have sound effects enabled for events such as
* clicking and touching.
*
@@ -17117,9 +17159,19 @@
@CallSuper
protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
- if (mStartActivityRequestWho != null) {
+ if (mStartActivityRequestWho != null || isAutofilled()) {
BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
+
+ if (mStartActivityRequestWho != null) {
+ state.mSavedData |= BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED;
+ }
+
+ if (isAutofilled()) {
+ state.mSavedData |= BaseSavedState.IS_AUTOFILLED;
+ }
+
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
+ state.mIsAutofilled = isAutofilled();
return state;
}
return BaseSavedState.EMPTY_STATE;
@@ -17189,7 +17241,14 @@
+ "other views do not use the same id.");
}
if (state != null && state instanceof BaseSavedState) {
- mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
+ BaseSavedState baseState = (BaseSavedState) state;
+
+ if ((baseState.mSavedData & BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED) != 0) {
+ mStartActivityRequestWho = baseState.mStartActivityRequestWhoSaved;
+ }
+ if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) {
+ setAutofilled(baseState.mIsAutofilled);
+ }
}
}
@@ -17570,6 +17629,7 @@
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
+ drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
@@ -17870,6 +17930,7 @@
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
+ drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
@@ -17946,6 +18007,7 @@
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
+ drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
@@ -18630,6 +18692,8 @@
// Step 4, draw the children
dispatchDraw(canvas);
+ drawAutofilledHighlight(canvas);
+
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
@@ -18783,6 +18847,8 @@
canvas.restoreToCount(saveCount);
+ drawAutofilledHighlight(canvas);
+
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
@@ -20141,6 +20207,41 @@
}
/**
+ * Get the drawable to be overlayed when a view is autofilled
+ *
+ * @return The drawable
+ *
+ * @throws IllegalStateException if the drawable could not be found.
+ */
+ @NonNull private Drawable getAutofilledDrawable() {
+ // Lazily load the isAutofilled drawable.
+ if (mAttachInfo.mAutofilledDrawable == null) {
+ mAttachInfo.mAutofilledDrawable = mContext.getDrawable(R.drawable.autofilled_highlight);
+
+ if (mAttachInfo.mAutofilledDrawable == null) {
+ throw new IllegalStateException(
+ "Could not find android:drawable/autofilled_highlight");
+ }
+ }
+
+ return mAttachInfo.mAutofilledDrawable;
+ }
+
+ /**
+ * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawAutofilledHighlight(@NonNull Canvas canvas) {
+ if (isAutofilled()) {
+ Drawable autofilledHighlight = getAutofilledDrawable();
+
+ autofilledHighlight.setBounds(0, 0, getWidth(), getHeight());
+ autofilledHighlight.draw(canvas);
+ }
+ }
+
+ /**
* Draw any foreground content for this view.
*
* <p>Foreground content may consist of scroll bars, a {@link #setForeground foreground}
@@ -24305,7 +24406,13 @@
* state in {@link android.view.View#onSaveInstanceState()}.
*/
public static class BaseSavedState extends AbsSavedState {
+ static final int START_ACTIVITY_REQUESTED_WHO_SAVED = 0b1;
+ static final int IS_AUTOFILLED = 0b10;
+
+ // Flags that describe what data in this state is valid
+ int mSavedData;
String mStartActivityRequestWhoSaved;
+ boolean mIsAutofilled;
/**
* Constructor used when reading from a parcel. Reads the state of the superclass.
@@ -24325,7 +24432,9 @@
*/
public BaseSavedState(Parcel source, ClassLoader loader) {
super(source, loader);
+ mSavedData = source.readInt();
mStartActivityRequestWhoSaved = source.readString();
+ mIsAutofilled = source.readBoolean();
}
/**
@@ -24340,7 +24449,10 @@
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
+
+ out.writeInt(mSavedData);
out.writeString(mStartActivityRequestWhoSaved);
+ out.writeBoolean(mIsAutofilled);
}
public static final Parcelable.Creator<BaseSavedState> CREATOR
@@ -24741,6 +24853,13 @@
Drawable mAccessibilityFocusDrawable;
/**
+ * The drawable for highlighting autofilled views.
+ *
+ * @see #isAutofilled()
+ */
+ Drawable mAutofilledDrawable;
+
+ /**
* Show where the margins, bounds and layout bounds are for each view.
*/
boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6b8aab6..666ccf4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.KeyguardManager;
import android.app.Presentation;
import android.content.Context;
@@ -1278,7 +1279,9 @@
/**
* Never animate position changes of the window.
*
- * {@hide} */
+ * {@hide}
+ */
+ @TestApi
public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 0x00000040;
/** Window flag: special flag to limit the size of the window to be
@@ -1387,6 +1390,7 @@
* Control flags that are private to the platform.
* @hide
*/
+ @TestApi
public int privateFlags;
/**
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 19980fb..07fad60 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -44,6 +44,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.Objects;
/**
* App entry point to the AutoFill Framework.
@@ -91,6 +92,8 @@
*/
public static final String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
+ static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
+
// Public flags start from the lowest bit
/**
* Indicates autofill was explicitly requested by the user.
@@ -115,6 +118,9 @@
private boolean mHasSession;
private boolean mEnabled;
+ /** If a view changes to this mapping the autofill operation was successful */
+ @Nullable private ParcelableMap mLastAutofilledData;
+
/** @hide */
public interface AutofillClient {
/**
@@ -160,7 +166,31 @@
}
/**
- * Checkes whether autofill is enabled for the current user.
+ * Restore state after activity lifecycle
+ *
+ * @param savedInstanceState The state to be restored
+ *
+ * {@hide}
+ */
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
+ }
+
+ /**
+ * Save state before activity lifecycle
+ *
+ * @param outState Place to store the state
+ *
+ * {@hide}
+ */
+ public void onSaveInstanceState(Bundle outState) {
+ if (mLastAutofilledData != null) {
+ outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
+ }
+ }
+
+ /**
+ * Checks whether autofill is enabled for the current user.
*
* <p>Typically used to determine whether the option to explicitly request autofill should
* be offered - see {@link #requestAutofill(View)}.
@@ -311,12 +341,43 @@
* @param view view whose value changed.
*/
public void notifyValueChanged(View view) {
+ AutofillId id = null;
+ boolean valueWasRead = false;
+ AutofillValue value = null;
+
+ // If the session is gone some fields might still be highlighted, hence we have to remove
+ // the isAutofilled property even if no sessions are active.
+ if (mLastAutofilledData == null) {
+ view.setAutofilled(false);
+ } else {
+ id = getAutofillId(view);
+ if (mLastAutofilledData.containsKey(id)) {
+ value = view.getAutofillValue();
+ valueWasRead = true;
+
+ if (Objects.equals(mLastAutofilledData.get(id), value)) {
+ view.setAutofilled(true);
+ } else {
+ view.setAutofilled(false);
+ mLastAutofilledData.remove(id);
+ }
+ } else {
+ view.setAutofilled(false);
+ }
+ }
+
if (!mEnabled || !mHasSession) {
return;
}
- final AutofillId id = getAutofillId(view);
- final AutofillValue value = view.getAutofillValue();
+ if (id == null) {
+ id = getAutofillId(view);
+ }
+
+ if (!valueWasRead) {
+ value = view.getAutofillValue();
+ }
+
updateSession(id, null, value, FLAG_VALUE_CHANGED);
}
@@ -535,6 +596,23 @@
}
}
+ /**
+ * Sets a view as autofilled if the current value is the {code targetValue}.
+ *
+ * @param view The view that is to be autofilled
+ * @param targetValue The value we want to fill into view
+ */
+ private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
+ AutofillValue currentValue = view.getAutofillValue();
+ if (Objects.equals(currentValue, targetValue)) {
+ if (mLastAutofilledData == null) {
+ mLastAutofilledData = new ParcelableMap(1);
+ }
+ mLastAutofilledData.put(getAutofillId(view), targetValue);
+ view.setAutofilled(true);
+ }
+ }
+
private void handleAutofill(IBinder windowToken, List<AutofillId> ids,
List<AutofillValue> values) {
final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
@@ -568,7 +646,19 @@
}
valuesByParent.put(id.getVirtualChildId(), value);
} else {
+ // Mark the view as to be autofilled with 'value'
+ if (mLastAutofilledData == null) {
+ mLastAutofilledData = new ParcelableMap(itemCount - i);
+ }
+ mLastAutofilledData.put(id, value);
+
view.autofill(value);
+
+ // Set as autofilled if the values match now, e.g. when the value was updated
+ // synchronously.
+ // If autofill happens async, the view is set to autofilled in notifyValueChanged.
+ setAutofilledIfValuesIs(view, value);
+
numApplied++;
}
}
diff --git a/core/java/android/view/autofill/ParcelableMap.java b/core/java/android/view/autofill/ParcelableMap.java
new file mode 100644
index 0000000..f97b1a0
--- /dev/null
+++ b/core/java/android/view/autofill/ParcelableMap.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A parcelable HashMap for {@link AutofillId} and {@link AutofillValue}
+ *
+ * {@hide}
+ */
+class ParcelableMap extends HashMap<AutofillId, AutofillValue> implements Parcelable {
+ ParcelableMap(int size) {
+ super(size);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(size());
+
+ for (Map.Entry<AutofillId, AutofillValue> entry : entrySet()) {
+ dest.writeParcelable(entry.getKey(), 0);
+ dest.writeParcelable(entry.getValue(), 0);
+ }
+ }
+
+ public static final Parcelable.Creator<ParcelableMap> CREATOR =
+ new Parcelable.Creator<ParcelableMap>() {
+ @Override
+ public ParcelableMap createFromParcel(Parcel source) {
+ int size = source.readInt();
+
+ ParcelableMap map = new ParcelableMap(size);
+
+ for (int i = 0; i < size; i++) {
+ AutofillId key = source.readParcelable(null);
+ AutofillValue value = source.readParcelable(null);
+
+ map.put(key, value);
+ }
+
+ return map;
+ }
+
+ @Override
+ public ParcelableMap[] newArray(int size) {
+ return new ParcelableMap[size];
+ }
+ };
+}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index ab4cce4..2e8faee 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -76,6 +76,7 @@
private boolean mDropDownVerticalOffsetSet;
private boolean mIsAnimatedFromAnchor = true;
private boolean mOverlapAnchor;
+ private boolean mOverlapAnchorSet;
private int mDropDownGravity = Gravity.NO_GRAVITY;
@@ -681,7 +682,9 @@
mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
mPopup.setTouchInterceptor(mTouchInterceptor);
mPopup.setEpicenterBounds(mEpicenterBounds);
- mPopup.setOverlapAnchor(mOverlapAnchor);
+ if (mOverlapAnchorSet) {
+ mPopup.setOverlapAnchor(mOverlapAnchor);
+ }
mPopup.showAsDropDown(getAnchorView(), mDropDownHorizontalOffset,
mDropDownVerticalOffset, mDropDownGravity);
mDropDownList.setSelection(ListView.INVALID_POSITION);
@@ -1259,6 +1262,7 @@
* @hide
*/
public void setOverlapAnchor(boolean overlap) {
+ mOverlapAnchorSet = true;
mOverlapAnchor = overlap;
}
diff --git a/core/java/com/android/internal/app/LRResolverRankerService.java b/core/java/com/android/internal/app/LRResolverRankerService.java
new file mode 100644
index 0000000..1cad7c7
--- /dev/null
+++ b/core/java/com/android/internal/app/LRResolverRankerService.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.storage.StorageManager;
+import android.service.resolver.ResolverRankerService;
+import android.service.resolver.ResolverTarget;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A Logistic Regression based {@link android.service.resolver.ResolverRankerService}, to be used
+ * in {@link ResolverComparator}.
+ */
+public final class LRResolverRankerService extends ResolverRankerService {
+ private static final String TAG = "LRResolverRankerService";
+
+ private static final boolean DEBUG = false;
+
+ private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
+ private static final String BIAS_PREF_KEY = "bias";
+ private static final String VERSION_PREF_KEY = "version";
+
+ private static final String LAUNCH_SCORE = "launch";
+ private static final String TIME_SPENT_SCORE = "timeSpent";
+ private static final String RECENCY_SCORE = "recency";
+ private static final String CHOOSER_SCORE = "chooser";
+
+ // parameters for a pre-trained model, to initialize the app ranker. When updating the
+ // pre-trained model, please update these params, as well as initModel().
+ private static final int CURRENT_VERSION = 1;
+ private static final float LEARNING_RATE = 0.0001f;
+ private static final float REGULARIZER_PARAM = 0.0001f;
+
+ private SharedPreferences mParamSharedPref;
+ private ArrayMap<String, Float> mFeatureWeights;
+ private float mBias;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ initModel();
+ return super.onBind(intent);
+ }
+
+ @Override
+ public void onPredictSharingProbabilities(List<ResolverTarget> targets) {
+ final int size = targets.size();
+ for (int i = 0; i < size; ++i) {
+ ResolverTarget target = targets.get(i);
+ ArrayMap<String, Float> features = getFeatures(target);
+ target.setSelectProbability(predict(features));
+ }
+ }
+
+ @Override
+ public void onTrainRankingModel(List<ResolverTarget> targets, int selectedPosition) {
+ final int size = targets.size();
+ if (selectedPosition < 0 || selectedPosition >= size) {
+ if (DEBUG) {
+ Log.d(TAG, "Invalid Position of Selected App " + selectedPosition);
+ }
+ return;
+ }
+ final ArrayMap<String, Float> positive = getFeatures(targets.get(selectedPosition));
+ final float positiveProbability = targets.get(selectedPosition).getSelectProbability();
+ final int targetSize = targets.size();
+ for (int i = 0; i < targetSize; ++i) {
+ if (i == selectedPosition) {
+ continue;
+ }
+ final ArrayMap<String, Float> negative = getFeatures(targets.get(i));
+ final float negativeProbability = targets.get(i).getSelectProbability();
+ if (negativeProbability > positiveProbability) {
+ update(negative, negativeProbability, false);
+ update(positive, positiveProbability, true);
+ }
+ }
+ commitUpdate();
+ }
+
+ private void initModel() {
+ mParamSharedPref = getParamSharedPref();
+ mFeatureWeights = new ArrayMap<>(4);
+ if (mParamSharedPref == null ||
+ mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
+ // Initializing the app ranker to a pre-trained model. When updating the pre-trained
+ // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
+ // REGULARIZER_PARAM.
+ mBias = -1.6568f;
+ mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
+ mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
+ mFeatureWeights.put(RECENCY_SCORE, 0.269f);
+ mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
+ } else {
+ mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
+ mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
+ mFeatureWeights.put(
+ TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
+ mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
+ mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
+ }
+ }
+
+ private ArrayMap<String, Float> getFeatures(ResolverTarget target) {
+ ArrayMap<String, Float> features = new ArrayMap<>(4);
+ features.put(RECENCY_SCORE, target.getRecencyScore());
+ features.put(TIME_SPENT_SCORE, target.getTimeSpentScore());
+ features.put(LAUNCH_SCORE, target.getLaunchScore());
+ features.put(CHOOSER_SCORE, target.getChooserScore());
+ return features;
+ }
+
+ private float predict(ArrayMap<String, Float> target) {
+ if (target == null) {
+ return 0.0f;
+ }
+ final int featureSize = target.size();
+ float sum = 0.0f;
+ for (int i = 0; i < featureSize; i++) {
+ String featureName = target.keyAt(i);
+ float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+ sum += weight * target.valueAt(i);
+ }
+ return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
+ }
+
+ private void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
+ if (target == null) {
+ return;
+ }
+ final int featureSize = target.size();
+ float error = isSelected ? 1.0f - predict : -predict;
+ for (int i = 0; i < featureSize; i++) {
+ String featureName = target.keyAt(i);
+ float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+ mBias += LEARNING_RATE * error;
+ currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
+ LEARNING_RATE * error * target.valueAt(i);
+ mFeatureWeights.put(featureName, currentWeight);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
+ }
+ }
+
+ private void commitUpdate() {
+ try {
+ SharedPreferences.Editor editor = mParamSharedPref.edit();
+ editor.putFloat(BIAS_PREF_KEY, mBias);
+ final int size = mFeatureWeights.size();
+ for (int i = 0; i < size; i++) {
+ editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
+ }
+ editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
+ editor.apply();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to commit update" + e);
+ }
+ }
+
+ private SharedPreferences getParamSharedPref() {
+ // The package info in the context isn't initialized in the way it is for normal apps,
+ // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
+ // build the path manually below using the same policy that appears in ContextImpl.
+ if (DEBUG) {
+ Log.d(TAG, "Context Package Name: " + getPackageName());
+ }
+ final File prefsFile = new File(new File(
+ Environment.getDataUserCePackageDirectory(
+ StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()),
+ "shared_prefs"),
+ PARAM_SHARED_PREF_NAME + ".xml");
+ return getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3f1c9ad..622b708 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -530,6 +530,9 @@
getMainThreadHandler().removeCallbacks(mPostListReadyRunnable);
mPostListReadyRunnable = null;
}
+ if (mAdapter != null && mAdapter.mResolverListController != null) {
+ mAdapter.mResolverListController.destroy();
+ }
}
@Override
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 096fcb8..73b62a5 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -26,20 +26,34 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.SharedPreferences;
+import android.content.ServiceConnection;
import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
import android.os.storage.StorageManager;
import android.os.UserHandle;
+import android.service.resolver.IResolverRankerService;
+import android.service.resolver.IResolverRankerResult;
+import android.service.resolver.ResolverRankerService;
+import android.service.resolver.ResolverTarget;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import java.io.File;
+import java.lang.InterruptedException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -61,11 +75,15 @@
private static final float RECENCY_MULTIPLIER = 2.f;
- // feature names used in ranking.
- private static final String LAUNCH_SCORE = "launch";
- private static final String TIME_SPENT_SCORE = "timeSpent";
- private static final String RECENCY_SCORE = "recency";
- private static final String CHOOSER_SCORE = "chooser";
+ // message types
+ private static final int RESOLVER_RANKER_SERVICE_RESULT = 0;
+ private static final int RESOLVER_RANKER_RESULT_TIMEOUT = 1;
+
+ // timeout for establishing connections with a ResolverRankerService.
+ private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200;
+ // timeout for establishing connections with a ResolverRankerService, collecting features and
+ // predicting ranking scores.
+ private static final int WATCHDOG_TIMEOUT_MILLIS = 500;
private final Collator mCollator;
private final boolean mHttp;
@@ -74,18 +92,74 @@
private final Map<String, UsageStats> mStats;
private final long mCurrentTime;
private final long mSinceTime;
- private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
+ private final LinkedHashMap<ComponentName, ResolverTarget> mTargetsDict = new LinkedHashMap<>();
private final String mReferrerPackage;
+ private final Object mLock = new Object();
+ private ArrayList<ResolverTarget> mTargets;
private String mContentType;
private String[] mAnnotations;
private String mAction;
- private LogisticRegressionAppRanker mRanker;
+ private IResolverRankerService mRanker;
+ private ResolverRankerServiceConnection mConnection;
+ private AfterCompute mAfterCompute;
+ private Context mContext;
+ private CountDownLatch mConnectSignal;
- public ResolverComparator(Context context, Intent intent, String referrerPackage) {
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case RESOLVER_RANKER_SERVICE_RESULT:
+ if (DEBUG) {
+ Log.d(TAG, "RESOLVER_RANKER_SERVICE_RESULT");
+ }
+ if (mHandler.hasMessages(RESOLVER_RANKER_RESULT_TIMEOUT)) {
+ if (msg.obj != null) {
+ final List<ResolverTarget> receivedTargets =
+ (List<ResolverTarget>) msg.obj;
+ if (receivedTargets != null && mTargets != null
+ && receivedTargets.size() == mTargets.size()) {
+ final int size = mTargets.size();
+ for (int i = 0; i < size; ++i) {
+ mTargets.get(i).setSelectProbability(
+ receivedTargets.get(i).getSelectProbability());
+ }
+ } else {
+ Log.e(TAG, "Sizes of sent and received ResolverTargets diff.");
+ }
+ } else {
+ Log.e(TAG, "Receiving null prediction results.");
+ }
+ mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
+ mAfterCompute.afterCompute();
+ }
+ break;
+
+ case RESOLVER_RANKER_RESULT_TIMEOUT:
+ if (DEBUG) {
+ Log.d(TAG, "RESOLVER_RANKER_RESULT_TIMEOUT; unbinding services");
+ }
+ mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
+ mAfterCompute.afterCompute();
+ break;
+
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ };
+
+ public interface AfterCompute {
+ public void afterCompute ();
+ }
+
+ public ResolverComparator(Context context, Intent intent, String referrerPackage,
+ AfterCompute afterCompute) {
mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
String scheme = intent.getScheme();
mHttp = "http".equals(scheme) || "https".equals(scheme);
mReferrerPackage = referrerPackage;
+ mAfterCompute = afterCompute;
+ mContext = context;
mPm = context.getPackageManager();
mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
@@ -96,9 +170,9 @@
mContentType = intent.getType();
getContentAnnotations(intent);
mAction = intent.getAction();
- mRanker = new LogisticRegressionAppRanker(context);
}
+ // get annotations of content from intent.
public void getContentAnnotations(Intent intent) {
ArrayList<String> annotations = intent.getStringArrayListExtra(
Intent.EXTRA_CONTENT_ANNOTATIONS);
@@ -114,20 +188,24 @@
}
}
+ public void setCallBack(AfterCompute afterCompute) {
+ mAfterCompute = afterCompute;
+ }
+
+ // compute features for each target according to usage stats of targets.
public void compute(List<ResolvedComponentInfo> targets) {
- mScoredTargets.clear();
+ reset();
final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD;
- long mostRecentlyUsedTime = recentSinceTime + 1;
- long mostTimeSpent = 1;
- int mostLaunched = 1;
- int mostSelected = 1;
+ float mostRecencyScore = 1.0f;
+ float mostTimeSpentScore = 1.0f;
+ float mostLaunchScore = 1.0f;
+ float mostChooserScore = 1.0f;
for (ResolvedComponentInfo target : targets) {
- final ScoredTarget scoredTarget
- = new ScoredTarget(target.getResolveInfoAt(0).activityInfo);
- mScoredTargets.put(target.name, scoredTarget);
+ final ResolverTarget resolverTarget = new ResolverTarget();
+ mTargetsDict.put(target.name, resolverTarget);
final UsageStats pkStats = mStats.get(target.name.getPackageName());
if (pkStats != null) {
// Only count recency for apps that weren't the caller
@@ -135,31 +213,33 @@
// Persistent processes muck this up, so omit them too.
if (!target.name.getPackageName().equals(mReferrerPackage)
&& !isPersistentProcess(target)) {
- final long lastTimeUsed = pkStats.getLastTimeUsed();
- scoredTarget.lastTimeUsed = lastTimeUsed;
- if (lastTimeUsed > mostRecentlyUsedTime) {
- mostRecentlyUsedTime = lastTimeUsed;
+ final float recencyScore =
+ (float) Math.max(pkStats.getLastTimeUsed() - recentSinceTime, 0);
+ resolverTarget.setRecencyScore(recencyScore);
+ if (recencyScore > mostRecencyScore) {
+ mostRecencyScore = recencyScore;
}
}
- final long timeSpent = pkStats.getTotalTimeInForeground();
- scoredTarget.timeSpent = timeSpent;
- if (timeSpent > mostTimeSpent) {
- mostTimeSpent = timeSpent;
+ final float timeSpentScore = (float) pkStats.getTotalTimeInForeground();
+ resolverTarget.setTimeSpentScore(timeSpentScore);
+ if (timeSpentScore > mostTimeSpentScore) {
+ mostTimeSpentScore = timeSpentScore;
}
- final int launched = pkStats.mLaunchCount;
- scoredTarget.launchCount = launched;
- if (launched > mostLaunched) {
- mostLaunched = launched;
+ final float launchScore = (float) pkStats.mLaunchCount;
+ resolverTarget.setLaunchScore(launchScore);
+ if (launchScore > mostLaunchScore) {
+ mostLaunchScore = launchScore;
}
- int selected = 0;
+ float chooserScore = 0.0f;
if (pkStats.mChooserCounts != null && mAction != null
&& pkStats.mChooserCounts.get(mAction) != null) {
- selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
+ chooserScore = (float) pkStats.mChooserCounts.get(mAction)
+ .getOrDefault(mContentType, 0);
if (mAnnotations != null) {
final int size = mAnnotations.length;
for (int i = 0; i < size; i++) {
- selected += pkStats.mChooserCounts.get(mAction)
+ chooserScore += (float) pkStats.mChooserCounts.get(mAction)
.getOrDefault(mAnnotations[i], 0);
}
}
@@ -169,44 +249,37 @@
Log.d(TAG, "Action type is null");
} else {
Log.d(TAG, "Chooser Count of " + mAction + ":" +
- target.name.getPackageName() + " is " + Integer.toString(selected));
+ target.name.getPackageName() + " is " +
+ Float.toString(chooserScore));
}
}
- scoredTarget.chooserCount = selected;
- if (selected > mostSelected) {
- mostSelected = selected;
+ resolverTarget.setChooserScore(chooserScore);
+ if (chooserScore > mostChooserScore) {
+ mostChooserScore = chooserScore;
}
}
}
-
if (DEBUG) {
- Log.d(TAG, "compute - mostRecentlyUsedTime: " + mostRecentlyUsedTime
- + " mostTimeSpent: " + mostTimeSpent
- + " recentSinceTime: " + recentSinceTime
- + " mostLaunched: " + mostLaunched);
+ Log.d(TAG, "compute - mostRecencyScore: " + mostRecencyScore
+ + " mostTimeSpentScore: " + mostTimeSpentScore
+ + " mostLaunchScore: " + mostLaunchScore
+ + " mostChooserScore: " + mostChooserScore);
}
- for (ScoredTarget target : mScoredTargets.values()) {
- final float recency = (float) Math.max(target.lastTimeUsed - recentSinceTime, 0)
- / (mostRecentlyUsedTime - recentSinceTime);
- target.setFeatures((float) target.launchCount / mostLaunched,
- (float) target.timeSpent / mostTimeSpent,
- recency * recency * RECENCY_MULTIPLIER,
- (float) target.chooserCount / mostSelected);
- target.selectProb = mRanker.predict(target.getFeatures());
+ mTargets = new ArrayList<>(mTargetsDict.values());
+ for (ResolverTarget target : mTargets) {
+ final float recency = target.getRecencyScore() / mostRecencyScore;
+ setFeatures(target, recency * recency * RECENCY_MULTIPLIER,
+ target.getLaunchScore() / mostLaunchScore,
+ target.getTimeSpentScore() / mostTimeSpentScore,
+ target.getChooserScore() / mostChooserScore);
+ addDefaultSelectProbability(target);
if (DEBUG) {
Log.d(TAG, "Scores: " + target);
}
}
- }
-
- static boolean isPersistentProcess(ResolvedComponentInfo rci) {
- if (rci != null && rci.getCount() > 0) {
- return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags &
- ApplicationInfo.FLAG_PERSISTENT) != 0;
- }
- return false;
+ predictSelectProbabilities(mTargets);
}
@Override
@@ -245,16 +318,16 @@
// Pinned items stay stable within a normal lexical sort and ignore scoring.
if (!lPinned && !rPinned) {
if (mStats != null) {
- final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName(
+ final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName(
lhs.activityInfo.packageName, lhs.activityInfo.name));
- final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
+ final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName(
rhs.activityInfo.packageName, rhs.activityInfo.name));
- final int selectProbDiff = Float.compare(
- rhsTarget.selectProb, lhsTarget.selectProb);
+ final int selectProbabilityDiff = Float.compare(
+ rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
- if (selectProbDiff != 0) {
- return selectProbDiff > 0 ? 1 : -1;
+ if (selectProbabilityDiff != 0) {
+ return selectProbabilityDiff > 0 ? 1 : -1;
}
}
}
@@ -268,177 +341,234 @@
}
public float getScore(ComponentName name) {
- final ScoredTarget target = mScoredTargets.get(name);
+ final ResolverTarget target = mTargetsDict.get(name);
if (target != null) {
- return target.selectProb;
+ return target.getSelectProbability();
}
return 0;
}
- static class ScoredTarget {
- public final ComponentInfo componentInfo;
- public long lastTimeUsed;
- public long timeSpent;
- public long launchCount;
- public long chooserCount;
- public ArrayMap<String, Float> features;
- public float selectProb;
-
- public ScoredTarget(ComponentInfo ci) {
- componentInfo = ci;
- features = new ArrayMap<>(5);
- }
-
- @Override
- public String toString() {
- return "ScoredTarget{" + componentInfo
- + " lastTimeUsed: " + lastTimeUsed
- + " timeSpent: " + timeSpent
- + " launchCount: " + launchCount
- + " chooserCount: " + chooserCount
- + " selectProb: " + selectProb
- + "}";
- }
-
- public void setFeatures(float launchCountScore, float usageTimeScore, float recencyScore,
- float chooserCountScore) {
- features.put(LAUNCH_SCORE, launchCountScore);
- features.put(TIME_SPENT_SCORE, usageTimeScore);
- features.put(RECENCY_SCORE, recencyScore);
- features.put(CHOOSER_SCORE, chooserCountScore);
- }
-
- public ArrayMap<String, Float> getFeatures() {
- return features;
- }
- }
-
public void updateChooserCounts(String packageName, int userId, String action) {
if (mUsm != null) {
mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action);
}
}
+ // update ranking model when the connection to it is valid.
public void updateModel(ComponentName componentName) {
- if (mScoredTargets == null || componentName == null ||
- !mScoredTargets.containsKey(componentName)) {
- return;
- }
- ScoredTarget selected = mScoredTargets.get(componentName);
- for (ComponentName targetComponent : mScoredTargets.keySet()) {
- if (targetComponent.equals(componentName)) {
- continue;
- }
- ScoredTarget target = mScoredTargets.get(targetComponent);
- // A potential point of optimization. Save updates or derive a closed form for the
- // positive case, to avoid calculating them repeatedly.
- if (target.selectProb >= selected.selectProb) {
- mRanker.update(target.getFeatures(), target.selectProb, false);
- mRanker.update(selected.getFeatures(), selected.selectProb, true);
+ synchronized (mLock) {
+ if (mRanker != null) {
+ try {
+ int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet())
+ .indexOf(componentName);
+ if (selectedPos > 0) {
+ mRanker.train(mTargets, selectedPos);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Selected a unknown component: " + componentName);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in Train: " + e);
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Ranker is null; skip updateModel.");
+ }
}
}
- mRanker.commitUpdate();
}
- class LogisticRegressionAppRanker {
- private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
- private static final String BIAS_PREF_KEY = "bias";
- private static final String VERSION_PREF_KEY = "version";
-
- // parameters for a pre-trained model, to initialize the app ranker. When updating the
- // pre-trained model, please update these params, as well as initModel().
- private static final int CURRENT_VERSION = 1;
- private static final float LEARNING_RATE = 0.0001f;
- private static final float REGULARIZER_PARAM = 0.0001f;
-
- private SharedPreferences mParamSharedPref;
- private ArrayMap<String, Float> mFeatureWeights;
- private float mBias;
-
- public LogisticRegressionAppRanker(Context context) {
- mParamSharedPref = getParamSharedPref(context);
- initModel();
+ // unbind the service and clear unhandled messges.
+ public void destroy() {
+ mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
+ mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
+ if (mConnection != null) {
+ mContext.unbindService(mConnection);
+ mConnection.destroy();
}
-
- public float predict(ArrayMap<String, Float> target) {
- if (target == null) {
- return 0.0f;
- }
- final int featureSize = target.size();
- float sum = 0.0f;
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
- sum += weight * target.valueAt(i);
- }
- return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
+ if (DEBUG) {
+ Log.d(TAG, "Unbinded Resolver Ranker.");
}
+ }
- public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
- if (target == null) {
+ // connect to a ranking service.
+ private void initRanker(Context context) {
+ synchronized (mLock) {
+ if (mConnection != null && mRanker != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Ranker still exists; reusing the existing one.");
+ }
return;
}
- final int featureSize = target.size();
- float error = isSelected ? 1.0f - predict : -predict;
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
- mBias += LEARNING_RATE * error;
- currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
- LEARNING_RATE * error * target.valueAt(i);
- mFeatureWeights.put(featureName, currentWeight);
+ }
+ Intent intent = resolveRankerService();
+ if (intent == null) {
+ return;
+ }
+ mConnectSignal = new CountDownLatch(1);
+ mConnection = new ResolverRankerServiceConnection(mConnectSignal);
+ context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
+ }
+
+ // resolve the service for ranking.
+ private Intent resolveRankerService() {
+ Intent intent = new Intent(ResolverRankerService.SERVICE_INTERFACE);
+ final List<ResolveInfo> resolveInfos = mPm.queryIntentServices(intent, 0);
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ if (resolveInfo == null || resolveInfo.serviceInfo == null
+ || resolveInfo.serviceInfo.applicationInfo == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Failed to retrieve a ranker: " + resolveInfo);
+ }
+ continue;
+ }
+ ComponentName componentName = new ComponentName(
+ resolveInfo.serviceInfo.applicationInfo.packageName,
+ resolveInfo.serviceInfo.name);
+ try {
+ final String perm = mPm.getServiceInfo(componentName, 0).permission;
+ if (!ResolverRankerService.BIND_PERMISSION.equals(perm)) {
+ Log.w(TAG, "ResolverRankerService " + componentName + " does not require"
+ + " permission " + ResolverRankerService.BIND_PERMISSION
+ + " - this service will not be queried for ResolverComparator."
+ + " add android:permission=\""
+ + ResolverRankerService.BIND_PERMISSION + "\""
+ + " to the <service> tag for " + componentName
+ + " in the manifest.");
+ continue;
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Could not look up service " + componentName
+ + "; component name not found");
+ continue;
}
if (DEBUG) {
- Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
+ Log.d(TAG, "Succeeded to retrieve a ranker: " + componentName);
}
+ intent.setComponent(componentName);
+ return intent;
+ }
+ return null;
+ }
+
+ // set a watchdog, to avoid waiting for ranking service for too long.
+ private void startWatchDog(int timeOutLimit) {
+ if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + timeOutLimit + "ms");
+ if (mHandler == null) {
+ Log.d(TAG, "Error: Handler is Null; Needs to be initialized.");
+ }
+ mHandler.sendEmptyMessageDelayed(RESOLVER_RANKER_RESULT_TIMEOUT, timeOutLimit);
+ }
+
+ private class ResolverRankerServiceConnection implements ServiceConnection {
+ private final CountDownLatch mConnectSignal;
+
+ public ResolverRankerServiceConnection(CountDownLatch connectSignal) {
+ mConnectSignal = connectSignal;
}
- public void commitUpdate() {
- SharedPreferences.Editor editor = mParamSharedPref.edit();
- editor.putFloat(BIAS_PREF_KEY, mBias);
- final int size = mFeatureWeights.size();
- for (int i = 0; i < size; i++) {
- editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
+ public final IResolverRankerResult resolverRankerResult =
+ new IResolverRankerResult.Stub() {
+ @Override
+ public void sendResult(List<ResolverTarget> targets) throws RemoteException {
+ if (DEBUG) {
+ Log.d(TAG, "Sending Result back to Resolver: " + targets);
+ }
+ synchronized (mLock) {
+ final Message msg = Message.obtain();
+ msg.what = RESOLVER_RANKER_SERVICE_RESULT;
+ msg.obj = targets;
+ mHandler.sendMessage(msg);
+ }
}
- editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
- editor.apply();
- }
+ };
- private SharedPreferences getParamSharedPref(Context context) {
- // The package info in the context isn't initialized in the way it is for normal apps,
- // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
- // build the path manually below using the same policy that appears in ContextImpl.
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) {
- Log.d(TAG, "Context Package Name: " + context.getPackageName());
+ Log.d(TAG, "onServiceConnected: " + name);
}
- final File prefsFile = new File(new File(
- Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
- context.getUserId(), context.getPackageName()),
- "shared_prefs"),
- PARAM_SHARED_PREF_NAME + ".xml");
- return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ synchronized (mLock) {
+ mRanker = IResolverRankerService.Stub.asInterface(service);
+ mConnectSignal.countDown();
+ }
}
- private void initModel() {
- mFeatureWeights = new ArrayMap<>(4);
- if (mParamSharedPref == null ||
- mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
- // Initializing the app ranker to a pre-trained model. When updating the pre-trained
- // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
- // REGULARIZER_PARAM.
- mBias = -1.6568f;
- mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
- mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
- mFeatureWeights.put(RECENCY_SCORE, 0.269f);
- mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
- } else {
- mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
- mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
- mFeatureWeights.put(
- TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
- mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
- mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) {
+ Log.d(TAG, "onServiceDisconnected: " + name);
+ }
+ synchronized (mLock) {
+ destroy();
}
}
+
+ public void destroy() {
+ synchronized (mLock) {
+ mRanker = null;
+ }
+ }
+ }
+
+ private void reset() {
+ mTargetsDict.clear();
+ mTargets = null;
+ startWatchDog(WATCHDOG_TIMEOUT_MILLIS);
+ initRanker(mContext);
+ }
+
+ // predict select probabilities if ranking service is valid.
+ private void predictSelectProbabilities(List<ResolverTarget> targets) {
+ if (mConnection == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Has not found valid ResolverRankerService; Skip Prediction");
+ }
+ return;
+ } else {
+ try {
+ mConnectSignal.await(CONNECTION_COST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ synchronized (mLock) {
+ if (mRanker != null) {
+ mRanker.predict(targets, mConnection.resolverRankerResult);
+ return;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Ranker has not been initialized; skip predict.");
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Error in Wait for Service Connection.");
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in Predict: " + e);
+ }
+ }
+ mAfterCompute.afterCompute();
+ }
+
+ // adds select prob as the default values, according to a pre-trained Logistic Regression model.
+ private void addDefaultSelectProbability(ResolverTarget target) {
+ float sum = 2.5543f * target.getLaunchScore() + 2.8412f * target.getTimeSpentScore() +
+ 0.269f * target.getRecencyScore() + 4.2222f * target.getChooserScore();
+ target.setSelectProbability((float) (1.0 / (1.0 + Math.exp(1.6568f - sum))));
+ }
+
+ // sets features for each target
+ private void setFeatures(ResolverTarget target, float recencyScore, float launchScore,
+ float timeSpentScore, float chooserScore) {
+ target.setRecencyScore(recencyScore);
+ target.setLaunchScore(launchScore);
+ target.setTimeSpentScore(timeSpentScore);
+ target.setChooserScore(chooserScore);
+ }
+
+ static boolean isPersistentProcess(ResolvedComponentInfo rci) {
+ if (rci != null && rci.getCount() > 0) {
+ return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags &
+ ApplicationInfo.FLAG_PERSISTENT) != 0;
+ }
+ return false;
}
}
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 4071ff4..e8bebb7 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -32,8 +32,10 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.InterruptedException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
import java.util.List;
/**
@@ -205,14 +207,42 @@
return listToReturn;
}
+ private class ComputeCallback implements ResolverComparator.AfterCompute {
+
+ private CountDownLatch mFinishComputeSignal;
+
+ public ComputeCallback(CountDownLatch finishComputeSignal) {
+ mFinishComputeSignal = finishComputeSignal;
+ }
+
+ public void afterCompute () {
+ mFinishComputeSignal.countDown();
+ }
+ }
+
@VisibleForTesting
@WorkerThread
public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) {
+ final CountDownLatch finishComputeSignal = new CountDownLatch(1);
+ ComputeCallback callback = new ComputeCallback(finishComputeSignal);
if (mResolverComparator == null) {
- mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
+ mResolverComparator =
+ new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, callback);
+ } else {
+ mResolverComparator.setCallBack(callback);
}
- mResolverComparator.compute(inputList);
- Collections.sort(inputList, mResolverComparator);
+ try {
+ long beforeRank = System.currentTimeMillis();
+ mResolverComparator.compute(inputList);
+ finishComputeSignal.await();
+ Collections.sort(inputList, mResolverComparator);
+ long afterRank = System.currentTimeMillis();
+ if (DEBUG) {
+ Log.d(TAG, "Time Cost: " + Long.toString(afterRank - beforeRank));
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Compute & Sort was interrupted: " + e);
+ }
}
private static boolean isSameResolvedComponent(ResolveInfo a,
@@ -233,7 +263,7 @@
@VisibleForTesting
public float getScore(ResolverActivity.DisplayResolveInfo target) {
if (mResolverComparator == null) {
- mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
+ return 0.0f;
}
return mResolverComparator.getScore(target.getResolvedComponentName());
}
@@ -249,4 +279,10 @@
mResolverComparator.updateChooserCounts(packageName, userId, action);
}
}
+
+ public void destroy() {
+ if (mResolverComparator != null) {
+ mResolverComparator.destroy();
+ }
+ }
}
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index a70209c..1abb59b 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -74,7 +74,6 @@
private final Rect mOldStableInsets = new Rect();
private final Rect mSystemInsets = new Rect();
private final Rect mStableInsets = new Rect();
- private final Rect mTmpRect = new Rect();
public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
@@ -371,6 +370,12 @@
DisplayListCanvas canvas = mSystemBarBackgroundNode.start(width, height);
mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top);
+ final int bottomInset = DecorView.getColorViewBottomInset(stableInsets.bottom,
+ systemInsets.bottom);
+ final int rightInset = DecorView.getColorViewRightInset(stableInsets.right,
+ systemInsets.right);
+ final int leftInset = DecorView.getColorViewLeftInset(stableInsets.left,
+ systemInsets.left);
if (mStatusBarColor != null) {
mStatusBarColor.setBounds(0, 0, left + width, topInset);
mStatusBarColor.draw(canvas);
@@ -380,8 +385,14 @@
// don't want the navigation bar background be moving around when resizing in docked mode.
// However, we need it for the transitions into/out of docked mode.
if (mNavigationBarColor != null && fullscreen) {
- DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect);
- mNavigationBarColor.setBounds(mTmpRect);
+ final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset);
+ if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) {
+ mNavigationBarColor.setBounds(width - size, 0, width, height);
+ } else if (DecorView.isNavBarToLeftEdge(bottomInset, leftInset)) {
+ mNavigationBarColor.setBounds(0, 0, size, height);
+ } else {
+ mNavigationBarColor.setBounds(0, height - size, width, height);
+ }
mNavigationBarColor.draw(canvas);
}
mSystemBarBackgroundNode.end(canvas);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 653796d..a8e16c9 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -119,21 +119,6 @@
// The height of a window which has not in DIP.
private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
- public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
- new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
- Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
- Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
- com.android.internal.R.id.statusBarBackground,
- FLAG_FULLSCREEN);
-
- public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
- new ColorViewAttributes(
- SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
- Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
- Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
- com.android.internal.R.id.navigationBarBackground,
- 0 /* hideWindowFlag */);
-
// Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
// size calculation takes the shadow size into account. We set the elevation currently
// to max until the first layout command has been executed.
@@ -177,10 +162,18 @@
// View added at runtime to draw under the navigation bar area
private View mNavigationGuard;
- private final ColorViewState mStatusColorViewState =
- new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
- private final ColorViewState mNavigationColorViewState =
- new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);
+ private final ColorViewState mStatusColorViewState = new ColorViewState(
+ SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
+ Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
+ Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
+ com.android.internal.R.id.statusBarBackground,
+ FLAG_FULLSCREEN);
+ private final ColorViewState mNavigationColorViewState = new ColorViewState(
+ SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
+ Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
+ Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
+ com.android.internal.R.id.navigationBarBackground,
+ 0 /* hideWindowFlag */);
private final Interpolator mShowInterpolator;
private final Interpolator mHideInterpolator;
@@ -990,50 +983,35 @@
return false;
}
- public static int getColorViewTopInset(int stableTop, int systemTop) {
+ static int getColorViewTopInset(int stableTop, int systemTop) {
return Math.min(stableTop, systemTop);
}
- public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
+ static int getColorViewBottomInset(int stableBottom, int systemBottom) {
return Math.min(stableBottom, systemBottom);
}
- public static int getColorViewRightInset(int stableRight, int systemRight) {
+ static int getColorViewRightInset(int stableRight, int systemRight) {
return Math.min(stableRight, systemRight);
}
- public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
+ static int getColorViewLeftInset(int stableLeft, int systemLeft) {
return Math.min(stableLeft, systemLeft);
}
- public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
+ static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
return bottomInset == 0 && rightInset > 0;
}
- public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
+ static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
return bottomInset == 0 && leftInset > 0;
}
- public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
+ static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
: isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
}
- public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
- Rect contentInsets, Rect outRect) {
- final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom);
- final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left);
- final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right);
- final int size = getNavBarSize(bottomInset, rightInset, leftInset);
- if (isNavBarToRightEdge(bottomInset, rightInset)) {
- outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
- } else if (isNavBarToLeftEdge(bottomInset, leftInset)) {
- outRect.set(0, 0, size, canvasHeight);
- } else {
- outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight);
- }
- }
-
WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
WindowManager.LayoutParams attrs = mWindow.getAttributes();
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
@@ -1153,14 +1131,9 @@
}
private int calculateStatusBarColor() {
- return calculateStatusBarColor(mWindow.getAttributes().flags,
- mSemiTransparentStatusBarColor, mWindow.mStatusBarColor);
- }
-
- public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor,
- int statusBarColor) {
- return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor
- : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor
+ int flags = mWindow.getAttributes().flags;
+ return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? mSemiTransparentStatusBarColor
+ : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? mWindow.mStatusBarColor
: Color.BLACK;
}
@@ -1187,9 +1160,13 @@
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
int size, boolean verticalBar, boolean seascape, int sideMargin,
boolean animate, boolean force) {
- state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
- boolean show = state.attributes.isVisible(state.present, color,
- mWindow.getAttributes().flags, force);
+ state.present = (sysUiVis & state.systemUiHideFlag) == 0
+ && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
+ && ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
+ || force);
+ boolean show = state.present
+ && (color & Color.BLACK) != 0
+ && ((mWindow.getAttributes().flags & state.translucentFlag) == 0 || force);
boolean showView = show && !isResizing() && size > 0;
boolean visibilityChanged = false;
@@ -1198,15 +1175,15 @@
int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
int resolvedGravity = verticalBar
- ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
- : state.attributes.verticalGravity;
+ ? (seascape ? state.seascapeGravity : state.horizontalGravity)
+ : state.verticalGravity;
if (view == null) {
if (showView) {
state.view = view = new View(mContext);
view.setBackgroundColor(color);
- view.setTransitionName(state.attributes.transitionName);
- view.setId(state.attributes.id);
+ view.setTransitionName(state.transitionName);
+ view.setId(state.id);
visibilityChanged = true;
view.setVisibility(INVISIBLE);
state.targetVisibility = VISIBLE;
@@ -2292,15 +2269,6 @@
boolean visible;
int color;
- final ColorViewAttributes attributes;
-
- ColorViewState(ColorViewAttributes attributes) {
- this.attributes = attributes;
- }
- }
-
- public static class ColorViewAttributes {
-
final int id;
final int systemUiHideFlag;
final int translucentFlag;
@@ -2310,9 +2278,9 @@
final String transitionName;
final int hideWindowFlag;
- private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
- int horizontalGravity, int seascapeGravity, String transitionName, int id,
- int hideWindowFlag) {
+ ColorViewState(int systemUiHideFlag,
+ int translucentFlag, int verticalGravity, int horizontalGravity,
+ int seascapeGravity, String transitionName, int id, int hideWindowFlag) {
this.id = id;
this.systemUiHideFlag = systemUiHideFlag;
this.translucentFlag = translucentFlag;
@@ -2322,24 +2290,6 @@
this.transitionName = transitionName;
this.hideWindowFlag = hideWindowFlag;
}
-
- public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
- return (sysUiVis & systemUiHideFlag) == 0
- && (windowFlags & hideWindowFlag) == 0
- && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
- || force);
- }
-
- public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
- return present
- && (color & Color.BLACK) != 0
- && ((windowFlags & translucentFlag) == 0 || force);
- }
-
- public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
- final boolean present = isPresent(sysUiVis, windowFlags, force);
- return isVisible(present, color, windowFlags, force);
- }
}
/**
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index da5d04d..33fabfc 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -208,6 +208,7 @@
$(TOP)/system/core/include \
$(TOP)/system/core/libappfuse/include \
$(TOP)/system/media/camera/include \
+ $(TOP)/system/media/private/camera/include \
$(TOP)/system/netd/include \
external/giflib \
external/pdfium/public \
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index 7b381b4..bfb25113 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -44,7 +44,7 @@
mWidth = width;
mHeight = height;
SkCanvas* canvas = mRecorder->beginRecording(SkIntToScalar(width), SkIntToScalar(height));
- return Canvas::create_canvas(canvas);
+ return Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer);
}
void Picture::endRecording() {
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index d233f7b..abc3599 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -21,6 +21,7 @@
#include "CreateJavaOutputStreamAdaptor.h"
+#include "SkColorSpaceXformCanvas.h"
#include "SkDocument.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
@@ -94,8 +95,10 @@
SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
&(page->mContentRect));
+ std::unique_ptr<SkCanvas> toSRGBCanvas =
+ SkCreateColorSpaceXformCanvas(canvas, SkColorSpace::MakeSRGB());
- canvas->drawPicture(page->mPicture);
+ toSRGBCanvas->drawPicture(page->mPicture);
document->endPage();
}
@@ -128,7 +131,7 @@
PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
contentLeft, contentTop, contentRight, contentBottom);
- return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
+ return reinterpret_cast<jlong>(Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer));
}
static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 78a5735..c11ce0f 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -36,6 +36,7 @@
#include <android/hardware/ICameraService.h>
#include <binder/IServiceManager.h>
#include <camera/CameraMetadata.h>
+#include <camera_metadata_hidden.h>
#include <camera/VendorTagDescriptor.h>
#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedPrimitiveArray.h>
@@ -162,8 +163,10 @@
extern "C" {
static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType);
-static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName);
-static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag);
+static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName, jlong vendorId);
+static jint CameraMetadata_getTagFromKeyLocal(JNIEnv *env, jobject thiz, jstring keyName);
+static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag, jlong vendorId);
+static jint CameraMetadata_getTypeFromTagLocal(JNIEnv *env, jobject thiz, jint tag);
static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz);
// Less safe access to native pointer. Does NOT throw any Java exceptions if NULL.
@@ -286,7 +289,9 @@
CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
if (metadata == NULL) return NULL;
- int tagType = get_camera_metadata_tag_type(tag);
+ const camera_metadata_t *metaBuffer = metadata->getAndLock();
+ int tagType = get_local_camera_metadata_tag_type(tag, metaBuffer);
+ metadata->unlock(metaBuffer);
if (tagType == -1) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
"Tag (%d) did not have a type", tag);
@@ -323,7 +328,9 @@
CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
if (metadata == NULL) return;
- int tagType = get_camera_metadata_tag_type(tag);
+ const camera_metadata_t *metaBuffer = metadata->getAndLock();
+ int tagType = get_local_camera_metadata_tag_type(tag, metaBuffer);
+ metadata->unlock(metaBuffer);
if (tagType == -1) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
"Tag (%d) did not have a type", tag);
@@ -528,14 +535,11 @@
static const JNINativeMethod gCameraMetadataMethods[] = {
// static methods
- { "nativeGetAllVendorKeys",
- "(Ljava/lang/Class;)Ljava/util/ArrayList;",
- (void *)CameraMetadata_getAllVendorKeys},
{ "nativeGetTagFromKey",
- "(Ljava/lang/String;)I",
+ "(Ljava/lang/String;J)I",
(void *)CameraMetadata_getTagFromKey },
{ "nativeGetTypeFromTag",
- "(I)I",
+ "(IJ)I",
(void *)CameraMetadata_getTypeFromTag },
{ "nativeSetupGlobalVendorTagDescriptor",
"()I",
@@ -559,6 +563,12 @@
{ "nativeSwap",
"(L" CAMERA_METADATA_CLASS_NAME ";)V",
(void *)CameraMetadata_swap },
+ { "nativeGetTagFromKeyLocal",
+ "(Ljava/lang/String;)I",
+ (void *)CameraMetadata_getTagFromKeyLocal },
+ { "nativeGetTypeFromTagLocal",
+ "(I)I",
+ (void *)CameraMetadata_getTypeFromTagLocal },
{ "nativeReadValues",
"(I)[B",
(void *)CameraMetadata_readValues },
@@ -568,6 +578,9 @@
{ "nativeDump",
"()V",
(void *)CameraMetadata_dump },
+ { "nativeGetAllVendorKeys",
+ "(Ljava/lang/Class;)Ljava/util/ArrayList;",
+ (void *)CameraMetadata_getAllVendorKeys},
// Parcelable interface
{ "nativeReadFromParcel",
"(Landroid/os/Parcel;)V",
@@ -590,11 +603,11 @@
gMetadataOffsets.mResultKey = MakeGlobalRefOrDie(env, resultKeyClazz);
gMetadataOffsets.mCharacteristicsConstr = GetMethodIDOrDie(env,
gMetadataOffsets.mCharacteristicsKey, "<init>",
- "(Ljava/lang/String;Ljava/lang/Class;)V");
+ "(Ljava/lang/String;Ljava/lang/Class;J)V");
gMetadataOffsets.mRequestConstr = GetMethodIDOrDie(env,
- gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V");
+ gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;J)V");
gMetadataOffsets.mResultConstr = GetMethodIDOrDie(env,
- gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V");
+ gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;J)V");
// Store global references for primitive array types used by Keys
jclass byteClazz = FindClassOrDie(env, "[B");
@@ -630,13 +643,76 @@
extern "C" {
-static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) {
+static jint CameraMetadata_getTypeFromTagLocal(JNIEnv *env, jobject thiz, jint tag) {
+ CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
+ metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID;
+ if (metadata) {
+ const camera_metadata_t *metaBuffer = metadata->getAndLock();
+ vendorId = get_camera_metadata_vendor_id(metaBuffer);
+ metadata->unlock(metaBuffer);
+ }
+ int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId);
+ if (tagType == -1) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Tag (%d) did not have a type", tag);
+ return -1;
+ }
+
+ return tagType;
+}
+
+static jint CameraMetadata_getTagFromKeyLocal(JNIEnv *env, jobject thiz, jstring keyName) {
+ ScopedUtfChars keyScoped(env, keyName);
+ const char *key = keyScoped.c_str();
+ if (key == NULL) {
+ // exception thrown by ScopedUtfChars
+ return 0;
+ }
+ ALOGV("%s (key = '%s')", __FUNCTION__, key);
+
+ uint32_t tag = 0;
+ sp<VendorTagDescriptor> vTags;
+ CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
+ if (metadata) {
+ sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+ if (cache.get()) {
+ const camera_metadata_t *metaBuffer = metadata->getAndLock();
+ metadata_vendor_id_t vendorId = get_camera_metadata_vendor_id(metaBuffer);
+ metadata->unlock(metaBuffer);
+ cache->getVendorTagDescriptor(vendorId, &vTags);
+ }
+ }
+
+ status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag);
+ if (res != OK) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Could not find tag for key '%s')", key);
+ }
+ return tag;
+}
+
+static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) {
+ metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID;
// Get all vendor tags
sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor();
if (vTags.get() == nullptr) {
- // No vendor tags.
- return NULL;
+ sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+ if (cache.get() == nullptr) {
+ // No vendor tags.
+ return nullptr;
+ }
+
+ CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+ if (metadata == NULL) return NULL;
+
+ const camera_metadata_t *metaBuffer = metadata->getAndLock();
+ vendorId = get_camera_metadata_vendor_id(metaBuffer);
+ cache->getVendorTagDescriptor(vendorId, &vTags);
+ metadata->unlock(metaBuffer);
+ if (vTags.get() == nullptr) {
+ return nullptr;
+ }
}
int count = vTags->getTagCount();
@@ -714,7 +790,7 @@
return NULL;
}
- jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz);
+ jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz, vendorId);
if (env->ExceptionCheck()) {
return NULL;
}
@@ -731,8 +807,8 @@
return arrayList;
}
-static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) {
-
+static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName,
+ jlong vendorId) {
ScopedUtfChars keyScoped(env, keyName);
const char *key = keyScoped.c_str();
if (key == NULL) {
@@ -744,6 +820,13 @@
uint32_t tag = 0;
sp<VendorTagDescriptor> vTags =
VendorTagDescriptor::getGlobalVendorTagDescriptor();
+ if (vTags.get() == nullptr) {
+ sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+ if (cache.get() != nullptr) {
+ cache->getVendorTagDescriptor(vendorId, &vTags);
+ }
+ }
+
status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag);
if (res != OK) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
@@ -752,8 +835,8 @@
return tag;
}
-static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) {
- int tagType = get_camera_metadata_tag_type(tag);
+static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag, jlong vendorId) {
+ int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId);
if (tagType == -1) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
"Tag (%d) did not have a type", tag);
@@ -787,8 +870,24 @@
__FUNCTION__, res.toString8().string());
return res.serviceSpecificErrorCode();
}
+ if (0 < desc->getTagCount()) {
+ err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);
+ } else {
+ sp<VendorTagDescriptorCache> cache = new VendorTagDescriptorCache();
+ binder::Status res = cameraService->getCameraVendorTagCache(/*out*/cache.get());
+ if (res.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_DISCONNECTED) {
+ // No camera module available, not an error on devices with no cameras
+ VendorTagDescriptorCache::clearGlobalVendorTagCache();
+ return OK;
+ } else if (!res.isOk()) {
+ VendorTagDescriptorCache::clearGlobalVendorTagCache();
+ ALOGE("%s: Failed to setup vendor tag cache: %s",
+ __FUNCTION__, res.toString8().string());
+ return res.serviceSpecificErrorCode();
+ }
- err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);
+ err = VendorTagDescriptorCache::setAsGlobalVendorTagCache(cache);
+ }
if (err != OK) {
return hardware::ICameraService::ERROR_INVALID_OPERATION;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7922250..cf6f37f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -914,7 +914,7 @@
android:permissionGroup="android.permission-group.PHONE"
android:label="@string/permlab_answerPhoneCalls"
android:description="@string/permdesc_answerPhoneCalls"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous|runtime" />
<!-- ====================================================================== -->
@@ -3130,6 +3130,15 @@
<permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
android:protectionLevel="signature" />
+ <!-- @SystemApi Must be required by services that extend
+ {@link android.service.resolver.ResolverRankerService}, to ensure that only the system can
+ bind to them.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a {@link
android.service.notification.ConditionProviderService},
to ensure that only the system can bind to it.
@@ -3641,6 +3650,13 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service android:name="com.android.internal.app.LRResolverRankerService"
+ android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE"
+ android:priority="-1" >
+ <intent-filter>
+ <action android:name="android.service.resolver.ResolverRankerService" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/core/res/res/drawable/autofilled_highlight.xml b/core/res/res/drawable/autofilled_highlight.xml
new file mode 100644
index 0000000..c7aacb9
--- /dev/null
+++ b/core/res/res/drawable/autofilled_highlight.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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.
+ -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="4dp"
+ android:insetRight="4dp"
+ android:insetBottom="4dp"
+ android:insetTop="4dp">
+ <shape>
+ <solid android:color="@color/autofilled_highlight" />
+ </shape>
+</inset>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d26d952..69c6fa4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8559,11 +8559,6 @@
<!-- @hide From Theme.colorBackground, used for the TaskDescription background
color. -->
<attr name="colorBackground" />
- <!-- @hide From Theme.statusBarColor, used for the TaskDescription status bar color. -->
- <attr name="statusBarColor"/>
- <!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar
- color. -->
- <attr name="navigationBarColor"/>
</declare-styleable>
<declare-styleable name="Shortcut">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index ed5a42b..3e4b66d7 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -245,6 +245,10 @@
<!-- Additional flag from base permission type: this permission can be granted to ephemeral
apps -->
<flag name="ephemeral" value="0x1000" />
+ <!-- Additional flag from base permission type: this permission can only be granted to apps
+ that target runtime permissions ({@link android.os.Build.VERSION_CODES#M} and above)
+ -->
+ <flag name="runtime" value="0x2000" />
</attr>
<!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index f9fd57c..937fc6f 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -150,6 +150,7 @@
<color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color>
<color name="accessibility_focus_highlight">#bf39b500</color>
+ <color name="autofilled_highlight">#4dffeb3b</color>
<color name="system_notification_accent_color">#ff607D8B</color>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 624eb59..1d1fd5e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2825,6 +2825,10 @@
<public name="autofill" />
</public-group>
+ <public-group type="drawable" first-id="0x010800b4">
+ <public name="autofilled_highlight" />
+ </public-group>
+
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 741115c..d0057d4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1333,6 +1333,7 @@
<java-symbol type="drawable" name="ic_sim_card_multi_48px_clr" />
<java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" />
+ <java-symbol type="drawable" name="autofilled_highlight"/>
<java-symbol type="drawable" name="ic_account_circle" />
<java-symbol type="color" name="user_icon_1" />
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index daf14af..13d7e09 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -23,6 +23,7 @@
#include "hwui/MinikinUtils.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include <SkColorSpaceXformCanvas.h>
#include <SkDrawable.h>
#include <SkDeque.h>
#include <SkDrawFilter.h>
@@ -44,18 +45,22 @@
return new SkiaCanvas(bitmap);
}
-Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
- return new SkiaCanvas(skiaCanvas);
+Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB) {
+ return new SkiaCanvas(skiaCanvas, xformToSRGB);
}
SkiaCanvas::SkiaCanvas() {}
-SkiaCanvas::SkiaCanvas(SkCanvas* canvas)
- : mCanvas(canvas) {}
+SkiaCanvas::SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB)
+ : mCanvas(canvas)
+{
+ LOG_ALWAYS_FATAL_IF(XformToSRGB::kImmediate == xformToSRGB);
+}
SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap));
- mCanvas = mCanvasOwned.get();
+ mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), SkColorSpace::MakeSRGB());
+ mCanvas = mCanvasWrapper.get();
}
SkiaCanvas::~SkiaCanvas() {}
@@ -92,19 +97,22 @@
};
void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
- SkCanvas* newCanvas = new SkCanvas(bitmap);
+ std::unique_ptr<SkCanvas> newCanvas = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap));
+ std::unique_ptr<SkCanvas> newCanvasWrapper =
+ SkCreateColorSpaceXformCanvas(newCanvas.get(), SkColorSpace::MakeSRGB());
if (!bitmap.isNull()) {
// Copy the canvas matrix & clip state.
- newCanvas->setMatrix(mCanvas->getTotalMatrix());
+ newCanvasWrapper->setMatrix(mCanvas->getTotalMatrix());
- ClipCopier copier(newCanvas);
+ ClipCopier copier(newCanvasWrapper.get());
mCanvas->replayClips(&copier);
}
// deletes the previously owned canvas (if any)
- mCanvasOwned = std::unique_ptr<SkCanvas>(newCanvas);
- mCanvas = newCanvas;
+ mCanvasOwned = std::move(newCanvas);
+ mCanvasWrapper = std::move(newCanvasWrapper);
+ mCanvas = mCanvasWrapper.get();
// clean up the old save stack
mSaveStack.reset(nullptr);
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 34c3717..13f979cf 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -37,8 +37,12 @@
* @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must
* not be NULL. This constructor does not take ownership, so the caller
* must guarantee that it remains valid while the SkiaCanvas is valid.
+ * @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB
+ * color space before drawing. This makes sense for software rendering.
+ * For the picture case, it may make more sense to leave bitmaps as is,
+ * and handle the xform when replaying the picture.
*/
- explicit SkiaCanvas(SkCanvas* canvas);
+ explicit SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB);
virtual ~SkiaCanvas();
@@ -181,6 +185,7 @@
class Clip;
+ std::unique_ptr<SkCanvas> mCanvasWrapper; // might own a wrapper on the canvas
std::unique_ptr<SkCanvas> mCanvasOwned; // might own a canvas we allocated
SkCanvas* mCanvas; // we do NOT own this canvas, it must survive us
// unless it is the same as mCanvasOwned.get()
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 969d877..ed32832 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -93,6 +93,15 @@
static WARN_UNUSED_RESULT Canvas* create_recording_canvas(int width, int height,
uirenderer::RenderNode* renderNode = nullptr);
+ enum class XformToSRGB {
+ // Transform any Bitmaps to the sRGB color space before drawing.
+ kImmediate,
+
+ // Draw the Bitmap as is. This likely means that we are recording and that the
+ // transform can be handled at playback time.
+ kDefer,
+ };
+
/**
* Create a new Canvas object which delegates to an SkCanvas.
*
@@ -100,10 +109,12 @@
* delegated to this object. This function will call ref() on the
* SkCanvas, and the returned Canvas will unref() it upon
* destruction.
+ * @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB
+ * color space before drawing.
* @return new non-null Canvas Object. The type of DisplayList produced by this canvas is
* determined based on Properties::getRenderPipelineType().
*/
- static Canvas* create_canvas(SkCanvas* skiaCanvas);
+ static Canvas* create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB);
/**
* Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 7fb75dc..44476af 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -44,7 +44,8 @@
// record the same text draw into a SkPicture and replay it into a Recording canvas
SkPictureRecorder recorder;
SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0);
- std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas));
+ std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas,
+ Canvas::XformToSRGB::kDefer));
TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25);
sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
@@ -63,7 +64,7 @@
TEST(SkiaCanvas, drawShadowLayer) {
auto surface = SkSurface::MakeRasterN32Premul(10, 10);
- SkiaCanvas canvas(surface->getCanvas());
+ SkiaCanvas canvas(surface->getCanvas(), Canvas::XformToSRGB::kDefer);
// clear to white
canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc);
@@ -78,3 +79,52 @@
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
ASSERT_NE(TestUtils::getColor(surface, 5, 5), SK_ColorWHITE);
}
+
+TEST(SkiaCanvas, colorSpaceXform) {
+ sk_sp<SkColorSpace> adobe = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+ SkColorSpace::kAdobeRGB_Gamut);
+
+ SkImageInfo adobeInfo = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType, adobe);
+ sk_sp<Bitmap> adobeBitmap = Bitmap::allocateHeapBitmap(adobeInfo);
+ SkBitmap adobeSkBitmap;
+ adobeBitmap->getSkBitmap(&adobeSkBitmap);
+ adobeSkBitmap.lockPixels();
+ *adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0; // Opaque, almost fully-red
+
+ SkImageInfo info = adobeInfo.makeColorSpace(nullptr);
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+
+ // Create a software canvas.
+ SkiaCanvas canvas(skBitmap);
+ canvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
+ // The result should be fully red, since we convert to sRGB at draw time.
+ skBitmap.lockPixels();
+ ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
+
+ // Now try in kDefer mode. This is a little strange given that, in practice, all software
+ // canvases are kImmediate.
+ SkCanvas skCanvas(skBitmap);
+ SkiaCanvas deferCanvas(&skCanvas, Canvas::XformToSRGB::kDefer);
+ deferCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
+ // The result should be as initialized, since we deferred the conversion to sRGB.
+ skBitmap.lockPixels();
+ ASSERT_EQ(0xFF0000F0, *skBitmap.getAddr32(0, 0));
+
+ // Test picture recording. We will kDefer the xform at recording time, but handle it when
+ // we playback to the software canvas.
+ SkPictureRecorder recorder;
+ SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
+ SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer);
+ picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
+ sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
+
+ // Playback to a deferred canvas. The result should be as initialized.
+ deferCanvas.asSkCanvas()->drawPicture(picture);
+ ASSERT_EQ(0xFF0000F0, *skBitmap.getAddr32(0, 0));
+
+ // Playback to an immediate canvas. The result should be fully red.
+ canvas.asSkCanvas()->drawPicture(picture);
+ ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
+}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 2723826..b8d1d12 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -753,9 +753,12 @@
* @param init container-specific data, its meaning is interpreted based on the
* mime type provided in the mimeType parameter. It could contain, for example,
* the content ID, key ID or other data obtained from the content metadata that is
- * required in generating the key request. init may be null when keyType is
- * KEY_TYPE_RELEASE.
- * @param mimeType identifies the mime type of the content
+ * required in generating the key request. May be null when keyType is
+ * KEY_TYPE_RELEASE or if the request is a renewal, i.e. not the first key
+ * request for the session.
+ * @param mimeType identifies the mime type of the content. May be null if the
+ * keyType is KEY_TYPE_RELEASE or if the request is a renewal, i.e. not the
+ * first key request for the session.
* @param keyType specifes the type of the request. The request may be to acquire
* keys for streaming or offline content, or to release previously acquired
* keys, which are identified by a keySetId.
@@ -779,13 +782,17 @@
* response is for an offline key request, a keySetId is returned that can be
* used to later restore the keys to a new session with the method
* {@link #restoreKeys}.
- * When the response is for a streaming or release request, null is returned.
+ * When the response is for a streaming or release request, an empty byte array
+ * is returned.
*
* @param scope may be a sessionId or keySetId depending on the type of the
* response. Scope should be set to the sessionId when the response is for either
* streaming or offline key requests. Scope should be set to the keySetId when
* the response is for a release request.
* @param response the byte array response from the server
+ * @return If the response is for an offline request, the keySetId for the offline
+ * keys will be returned. If the response is for a streaming or release request
+ * an empty byte array will be returned.
*
* @throws NotProvisionedException if the response indicates that
* reprovisioning is required
@@ -1014,13 +1021,13 @@
* Set a DRM engine plugin String property value.
*/
public native void setPropertyString(
- @StringProperty String propertyName, @NonNull String value);
+ String propertyName, @NonNull String value);
/**
* Set a DRM engine plugin byte array property value.
*/
public native void setPropertyByteArray(
- @ArrayProperty String propertyName, @NonNull byte[] value);
+ String propertyName, @NonNull byte[] value);
private static final native void setCipherAlgorithmNative(
@NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 23bf3d6..431d5d8 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -34,6 +34,7 @@
libutils \
libbinder \
libmedia \
+ libmediametrics \
libmediadrm \
libmidi \
libskia \
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index cd0e587..54442b3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -154,18 +154,18 @@
@SmallTest
public void testGetTypeFromTag() {
assertEquals(TYPE_BYTE,
- CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
+ CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE, Long.MAX_VALUE));
assertEquals(TYPE_RATIONAL,
- CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
+ CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM, Long.MAX_VALUE));
assertEquals(TYPE_FLOAT,
- CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
+ CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS, Long.MAX_VALUE));
assertEquals(TYPE_BYTE,
- CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
+ CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE, Long.MAX_VALUE));
assertEquals(TYPE_INT32,
- CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
+ CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, Long.MAX_VALUE));
try {
- CameraMetadataNative.getNativeType(0xDEADF00D);
+ CameraMetadataNative.getNativeType(0xDEADF00D, Long.MAX_VALUE);
fail("No type should exist for invalid tag 0xDEADF00D");
} catch(IllegalArgumentException e) {
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 77f2e19..a1c8de5 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -122,9 +122,6 @@
return true;
}
}
- for (BluetoothDevice src : srcs) {
- mService.disconnect(src);
- }
}
return mService.connect(device);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 9b699bc..169aac9 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -132,11 +132,6 @@
return true;
}
}
- // Handsfree HF only supports one source connection and hence it is OK to disconnect
- // the only connected device here.
- for (BluetoothDevice src : srcs) {
- mService.disconnect(src);
- }
}
return mService.connect(device);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index a7621fc..6efa468 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -115,10 +115,10 @@
public boolean connect(BluetoothDevice device) {
if (mService == null) return false;
List<BluetoothDevice> connectedDevices = getConnectedDevices();
- if (connectedDevices != null) {
- for (BluetoothDevice connectedDevice : connectedDevices) {
- mService.disconnect(connectedDevice);
- }
+ if (connectedDevices != null && connectedDevices.contains(device)) {
+ // Connect to same device, Ignore it
+ Log.d(TAG,"Ignoring Connect");
+ return true;
}
return mService.connect(device);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 72a3b3a..bd37abe 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -144,9 +144,6 @@
return true;
}
}
- for (BluetoothDevice src : srcs) {
- mService.disconnect(src);
- }
}
Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 4bfca9b..474de90 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -211,7 +211,16 @@
if (preferLongName) {
displayName = getZoneLongName(timeZoneNames, tz, now);
} else {
- displayName = timeZoneNames.getExemplarLocationName(tz.getID());
+ // Canonicalize the zone ID for ICU. It will only return valid strings for zone IDs
+ // that match ICUs zone IDs (which are similar but not guaranteed the same as those
+ // in timezones.xml). timezones.xml and related files uses the IANA IDs. ICU IDs are
+ // stable and IANA IDs have changed over time so they have drifted.
+ // See http://bugs.icu-project.org/trac/ticket/13070 / http://b/36469833.
+ String canonicalZoneId = android.icu.util.TimeZone.getCanonicalID(tz.getID());
+ if (canonicalZoneId == null) {
+ canonicalZoneId = tz.getID();
+ }
+ displayName = timeZoneNames.getExemplarLocationName(canonicalZoneId);
if (displayName == null || displayName.isEmpty()) {
// getZoneExemplarLocation can return null. Fall back to the long name.
displayName = getZoneLongName(timeZoneNames, tz, now);
@@ -325,4 +334,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 1ea4183..901848a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -149,6 +149,7 @@
private int mRankingScore = Integer.MIN_VALUE;
private int mBadge = NetworkBadging.BADGING_NONE;
+ private boolean mIsScoredNetworkMetered = false;
// used to co-relate internal vs returned accesspoint.
int mId;
@@ -248,6 +249,7 @@
this.mScanResultCache.putAll(that.mScanResultCache);
this.mId = that.mId;
this.mBadge = that.mBadge;
+ this.mIsScoredNetworkMetered = that.mIsScoredNetworkMetered;
this.mRankingScore = that.mRankingScore;
}
@@ -336,16 +338,32 @@
builder.append(",level=").append(getLevel());
builder.append(",rankingScore=").append(mRankingScore);
builder.append(",badge=").append(mBadge);
+ builder.append(",metered=").append(isMetered());
return builder.append(')').toString();
}
/**
+ * Updates the AccessPoint rankingScore, metering, and badge, returning true if the data has
+ * changed.
+ *
+ * @param scoreCache The score cache to use to retrieve scores.
+ * @param scoringUiEnabled Whether to show scoring and badging UI.
+ */
+ boolean update(WifiNetworkScoreCache scoreCache, boolean scoringUiEnabled) {
+ boolean scoreChanged = false;
+ if (scoringUiEnabled) {
+ scoreChanged = updateScores(scoreCache);
+ }
+ return updateMetered(scoreCache) || scoreChanged;
+ }
+
+ /**
* Updates the AccessPoint rankingScore and badge, returning true if the data has changed.
*
* @param scoreCache The score cache to use to retrieve scores.
*/
- boolean updateScores(WifiNetworkScoreCache scoreCache) {
+ private boolean updateScores(WifiNetworkScoreCache scoreCache) {
int oldBadge = mBadge;
int oldRankingScore = mRankingScore;
mBadge = NetworkBadging.BADGING_NONE;
@@ -366,6 +384,23 @@
return (oldBadge != mBadge || oldRankingScore != mRankingScore);
}
+ /**
+ * Updates the AccessPoint's metering based on {@link ScoredNetwork#meteredHint}, returning
+ * true if the metering changed.
+ */
+ private boolean updateMetered(WifiNetworkScoreCache scoreCache) {
+ boolean oldMetering = mIsScoredNetworkMetered;
+ mIsScoredNetworkMetered = false;
+ for (ScanResult result : mScanResultCache.values()) {
+ ScoredNetwork score = scoreCache.getScoredNetwork(result);
+ if (score == null) {
+ continue;
+ }
+ mIsScoredNetworkMetered |= score.meteredHint;
+ }
+ return oldMetering == mIsScoredNetworkMetered;
+ }
+
private void evictOldScanResults() {
long nowMs = SystemClock.elapsedRealtime();
for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
@@ -474,6 +509,17 @@
mSeen = seen;
}
+ /**
+ * Returns if the network is marked metered. Metering can be marked through its config in
+ * {@link WifiConfiguration}, after connection in {@link WifiInfo}, or from a score config in
+ * {@link ScoredNetwork}.
+ */
+ public boolean isMetered() {
+ return mIsScoredNetworkMetered
+ || (mConfig != null && mConfig.meteredHint)
+ || (mInfo != null && mInfo.getMeteredHint());
+ }
+
public NetworkInfo getNetworkInfo() {
return mNetworkInfo;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 8f8167e..e82bf81 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -183,7 +183,7 @@
}
if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
mFrictionSld.setState(STATE_SECURED);
- } else if (mAccessPoint.getConfig() != null && mAccessPoint.getConfig().meteredHint) {
+ } else if (mAccessPoint.isMetered()) {
mFrictionSld.setState(STATE_METERED);
}
Drawable drawable = mFrictionSld.getCurrent();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 50f294c..fc8c42c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -543,11 +543,9 @@
}
}
- if (mNetworkScoringUiEnabled) {
- requestScoresForNetworkKeys(scoresToRequest);
- for (AccessPoint ap : accessPoints) {
- ap.updateScores(mScoreCache);
- }
+ requestScoresForNetworkKeys(scoresToRequest);
+ for (AccessPoint ap : accessPoints) {
+ ap.update(mScoreCache, mNetworkScoringUiEnabled);
}
// Pre-sort accessPoints to speed preference insertion
@@ -648,7 +646,7 @@
if (ap.update(connectionConfig, mLastInfo, mLastNetworkInfo)) {
reorder = true;
}
- if (mNetworkScoringUiEnabled && ap.updateScores(mScoreCache)) {
+ if (ap.update(mScoreCache, mNetworkScoringUiEnabled)) {
reorder = true;
}
}
@@ -659,15 +657,11 @@
}
/**
- * Update all the internal access points rankingScores and badge.
+ * Update all the internal access points rankingScores, badge and metering.
*
* <p>Will trigger a resort and notify listeners of changes if applicable.
*/
private void updateNetworkScores() {
- if (!mNetworkScoringUiEnabled) {
- return;
- }
-
// Lock required to prevent accidental copying of AccessPoint states while the modification
// is in progress. see #copyAndNotifyListeners
long before = System.currentTimeMillis();
@@ -679,7 +673,7 @@
boolean reorder = false;
for (int i = 0; i < mInternalAccessPoints.size(); i++) {
- if (mInternalAccessPoints.get(i).updateScores(mScoreCache)) {
+ if (mInternalAccessPoints.get(i).update(mScoreCache, mNetworkScoringUiEnabled)) {
reorder = true;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
index ab7c6d2..a2becf7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
@@ -27,7 +27,7 @@
private static WifiTracker sTestingWifiTracker;
- @Keep
+ @Keep // Keep proguard from stripping this method since it is only used in tests
public static void setTestingWifiTracker(WifiTracker tracker) {
sTestingWifiTracker = tracker;
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 762d9f8..3e01b34 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -17,15 +17,19 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
+import android.net.NetworkKey;
+import android.net.ScoredNetwork;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiSsid;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.pps.HomeSp;
@@ -36,10 +40,11 @@
import android.support.test.runner.AndroidJUnit4;
import android.text.SpannableString;
import android.text.style.TtsSpan;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
@@ -50,9 +55,11 @@
private static final String TEST_SSID = "test_ssid";
private Context mContext;
+ @Mock private WifiNetworkScoreCache mWifiNetworkScoreCache;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getTargetContext();
}
@@ -74,6 +81,7 @@
@Test
public void testCopyAccessPoint_dataShouldMatch() {
WifiConfiguration configuration = createWifiConfiguration();
+ configuration.meteredHint = true;
NetworkInfo networkInfo =
new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
@@ -88,6 +96,7 @@
assertThat(originalAccessPoint.getBssid()).isEqualTo(copy.getBssid());
assertThat(originalAccessPoint.getConfig()).isEqualTo(copy.getConfig());
assertThat(originalAccessPoint.getSecurity()).isEqualTo(copy.getSecurity());
+ assertThat(originalAccessPoint.isMetered()).isEqualTo(copy.isMetered());
assertThat(originalAccessPoint.compareTo(copy) == 0).isTrue();
}
@@ -230,6 +239,55 @@
assertTrue(ap.isPasspointConfig());
}
+ @Test
+ public void testIsMetered_returnTrueWhenWifiConfigurationIsMetered() {
+ WifiConfiguration configuration = createWifiConfiguration();
+ configuration.meteredHint = true;
+
+ NetworkInfo networkInfo =
+ new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
+ AccessPoint accessPoint = new AccessPoint(mContext, configuration);
+ WifiInfo wifiInfo = new WifiInfo();
+ wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
+ wifiInfo.setBSSID(configuration.BSSID);
+ wifiInfo.setNetworkId(configuration.networkId);
+ accessPoint.update(configuration, wifiInfo, networkInfo);
+
+ assertTrue(accessPoint.isMetered());
+ };
+
+ @Test
+ public void testIsMetered_returnTrueWhenWifiInfoIsMetered() {
+ WifiConfiguration configuration = createWifiConfiguration();
+
+ NetworkInfo networkInfo =
+ new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
+ AccessPoint accessPoint = new AccessPoint(mContext, configuration);
+ WifiInfo wifiInfo = new WifiInfo();
+ wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
+ wifiInfo.setBSSID(configuration.BSSID);
+ wifiInfo.setNetworkId(configuration.networkId);
+ wifiInfo.setMeteredHint(true);
+ accessPoint.update(configuration, wifiInfo, networkInfo);
+
+ assertTrue(accessPoint.isMetered());
+ };
+
+ @Test
+ public void testIsMetered_returnTrueWhenScoredNetworkIsMetered() {
+ AccessPoint ap = createAccessPointWithScanResultCache();
+
+ when(mWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
+ .thenReturn(
+ new ScoredNetwork(
+ null /* NetworkKey */,
+ null /* rssiCurve */,
+ true /* metered */));
+ ap.update(mWifiNetworkScoreCache, false /* scoringUiEnabled */);
+
+ assertTrue(ap.isMetered());
+ };
+
private AccessPoint createAccessPointWithScanResultCache() {
Bundle bundle = new Bundle();
ArrayList<ScanResult> scanResults = new ArrayList<>();
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 02deb44..b71915f 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
@@ -302,7 +303,7 @@
new ScoredNetwork(
NETWORK_KEY_2,
mockCurve2,
- false /* meteredHint */,
+ true /* meteredHint */,
attr2);
WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue();
@@ -515,6 +516,23 @@
}
@Test
+ public void scoreCacheUpdateMeteredShouldUpdateAccessPointMetering()
+ throws InterruptedException {
+ WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
+ updateScoresAndWaitForAccessPointsChangedCallback();
+
+ List<AccessPoint> aps = tracker.getAccessPoints();
+
+ for (AccessPoint ap : aps) {
+ if (ap.getSsidStr().equals(SSID_1)) {
+ assertFalse(ap.isMetered());
+ } else if (ap.getSsidStr().equals(SSID_2)) {
+ assertTrue(ap.isMetered());
+ }
+ }
+ }
+
+ @Test
public void noBadgesShouldBeInsertedIntoAccessPointWhenScoringUiDisabled()
throws InterruptedException {
Settings.Global.putInt(
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1147f16..bf39bc4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -183,6 +183,9 @@
<!-- to control accessibility volume -->
<uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
+ <!-- to access ResolverRankerServices -->
+ <uses-permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 195eb9b..ff22ffb 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -97,6 +97,15 @@
android:layout_height="wrap_content"
android:text="@string/notification_channel_disabled"
style="@style/TextAppearance.NotificationInfo.Secondary" />
+ <!-- Optional link to app. Only appears if the channel is not disabled -->
+ <TextView
+ android:id="@+id/app_settings"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:ellipsize="end"
+ android:maxLines="1"
+ style="@style/TextAppearance.NotificationInfo.Secondary.Link"/>
</LinearLayout>
<!-- Ban Channel Switch -->
<Switch
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a8cf3da..43aeaa3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1472,6 +1472,9 @@
<!-- Notification: Control panel: Label for button that launches notification settings. Used
when this app has only defined a single channel for notifications. -->
<string name="notification_more_settings">More settings</string>
+ <!-- Notification: Control panel: Label for a link that launches notification settings in the
+ app that sent the notification. -->
+ <string name="notification_app_settings">Customize: <xliff:g id="sub_category" example="Work chats">%1$s</xliff:g></string>
<!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
<string name="notification_done">Done</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d6abda6..c9479b8 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -370,6 +370,10 @@
<item name="android:textColor">?android:attr/colorError</item>
</style>
+ <style name="TextAppearance.NotificationInfo.Secondary.Link">
+ <item name="android:textColor">?android:attr/colorAccent</item>
+ </style>
+
<style name="TextAppearance.NotificationInfo.Button">
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textSize">14sp</item>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 3a43d30..1c71da0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -355,10 +355,9 @@
rti.firstActiveTime = rti.lastActiveTime = i;
if (i % 2 == 0) {
rti.taskDescription = new ActivityManager.TaskDescription(description,
- Bitmap.createBitmap(mDummyIcon), null,
- 0xFF000000 | (0xFFFFFF & new Random().nextInt()),
- 0xFF000000 | (0xFFFFFF & new Random().nextInt()),
- 0, 0);
+ Bitmap.createBitmap(mDummyIcon), null,
+ 0xFF000000 | (0xFFFFFF & new Random().nextInt()),
+ 0xFF000000 | (0xFFFFFF & new Random().nextInt()));
} else {
rti.taskDescription = new ActivityManager.TaskDescription();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 8298f35..b4b1cd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -16,45 +16,34 @@
package com.android.systemui.statusbar;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
import android.app.INotificationManager;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
-import android.app.NotificationManager;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
+import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
-import android.view.View.OnClickListener;
-import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.NotificationGuts.OnSettingsClickListener;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.lang.IllegalArgumentException;
import java.util.List;
@@ -71,12 +60,16 @@
private int mAppUid;
private List<NotificationChannel> mNotificationChannels;
private NotificationChannel mSingleNotificationChannel;
+ private StatusBarNotification mSbn;
private int mStartingUserImportance;
private TextView mNumChannelsView;
private View mChannelDisabledView;
+ private TextView mSettingsLinkView;
private Switch mChannelEnabledSwitch;
private CheckSaveListener mCheckSaveListener;
+ private OnAppSettingsClickListener mAppSettingsClickListener;
+ private PackageManager mPm;
private NotificationGuts mGutsContainer;
@@ -95,11 +88,18 @@
void onClick(View v, NotificationChannel channel, int appUid);
}
+ public interface OnAppSettingsClickListener {
+ void onClick(View v, Intent intent);
+ }
+
public void bindNotification(final PackageManager pm,
final INotificationManager iNotificationManager,
final String pkg,
final List<NotificationChannel> notificationChannels,
+ int startingUserImportance,
+ final StatusBarNotification sbn,
OnSettingsClickListener onSettingsClick,
+ OnAppSettingsClickListener onAppSettingsClick,
OnClickListener onDoneClick,
CheckSaveListener checkSaveListener,
final Set<String> nonBlockablePkgs)
@@ -108,16 +108,21 @@
mPkg = pkg;
mNotificationChannels = notificationChannels;
mCheckSaveListener = checkSaveListener;
+ mSbn = sbn;
+ mPm = pm;
+ mAppSettingsClickListener = onAppSettingsClick;
boolean isSingleDefaultChannel = false;
+ mStartingUserImportance = startingUserImportance;
if (mNotificationChannels.isEmpty()) {
throw new IllegalArgumentException("bindNotification requires at least one channel");
- } else if (mNotificationChannels.size() == 1) {
- mSingleNotificationChannel = mNotificationChannels.get(0);
- mStartingUserImportance = mSingleNotificationChannel.getImportance();
- isSingleDefaultChannel = mSingleNotificationChannel.getId()
- .equals(NotificationChannel.DEFAULT_CHANNEL_ID);
- } else {
- mSingleNotificationChannel = null;
+ } else {
+ if (mNotificationChannels.size() == 1) {
+ mSingleNotificationChannel = mNotificationChannels.get(0);
+ isSingleDefaultChannel = mSingleNotificationChannel.getId()
+ .equals(NotificationChannel.DEFAULT_CHANNEL_ID);
+ } else {
+ mSingleNotificationChannel = null;
+ }
}
String appName = mPkg;
@@ -248,6 +253,9 @@
final TextView doneButton = (TextView) findViewById(R.id.done);
doneButton.setText(R.string.notification_done);
doneButton.setOnClickListener(onDoneClick);
+
+ // Optional settings link
+ updateAppSettingsLink();
}
private boolean hasImportanceChanged() {
@@ -276,7 +284,7 @@
private int getSelectedImportance() {
if (!mChannelEnabledSwitch.isChecked()) {
- return NotificationManager.IMPORTANCE_NONE;
+ return IMPORTANCE_NONE;
} else {
return mStartingUserImportance;
}
@@ -286,7 +294,7 @@
// Enabled Switch
mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch);
mChannelEnabledSwitch.setChecked(
- mStartingUserImportance != NotificationManager.IMPORTANCE_NONE);
+ mStartingUserImportance != IMPORTANCE_NONE);
final boolean visible = !nonBlockable && mSingleNotificationChannel != null;
mChannelEnabledSwitch.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
@@ -296,12 +304,13 @@
mGutsContainer.resetFalsingCheck();
}
updateSecondaryText();
+ updateAppSettingsLink();
});
}
private void updateSecondaryText() {
final boolean disabled = mSingleNotificationChannel != null &&
- getSelectedImportance() == NotificationManager.IMPORTANCE_NONE;
+ getSelectedImportance() == IMPORTANCE_NONE;
if (disabled) {
mChannelDisabledView.setVisibility(View.VISIBLE);
mNumChannelsView.setVisibility(View.GONE);
@@ -311,6 +320,45 @@
}
}
+ private void updateAppSettingsLink() {
+ mSettingsLinkView = findViewById(R.id.app_settings);
+ Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
+ mSbn.getId(), mSbn.getTag());
+ if (settingsIntent != null && getSelectedImportance() != IMPORTANCE_NONE
+ && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
+ mSettingsLinkView.setVisibility(View.VISIBLE);
+ mSettingsLinkView.setText(mContext.getString(R.string.notification_app_settings,
+ mSbn.getNotification().getSettingsText()));
+ mSettingsLinkView.setOnClickListener((View view) -> {
+ mAppSettingsClickListener.onClick(view, settingsIntent);
+ });
+ } else {
+ mSettingsLinkView.setVisibility(View.GONE);
+ }
+ }
+
+ private Intent getAppSettingsIntent(PackageManager pm, String packageName,
+ NotificationChannel channel, int id, String tag) {
+ Intent intent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
+ .setPackage(packageName);
+ final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
+ intent,
+ PackageManager.MATCH_DEFAULT_ONLY
+ );
+ if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
+ return null;
+ }
+ final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
+ intent.setClassName(activityInfo.packageName, activityInfo.name);
+ if (channel != null) {
+ intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
+ }
+ intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
+ intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
+ return intent;
+ }
+
@Override
public void setGutsParent(NotificationGuts guts) {
mGutsContainer = guts;
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 472af65..9304de5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -48,8 +48,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -5701,7 +5703,7 @@
.findViewById(com.android.internal.R.id.media_actions) != null;
}
- // The (i) button in the guts that links to the system notification settings for that app
+ // The button in the guts that links to the system notification settings for that app
private void startAppNotificationSettingsActivity(String packageName, final int appUid,
final NotificationChannel channel) {
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
@@ -5781,10 +5783,17 @@
startAppNotificationSettingsActivity(pkg, appUid, channel);
};
}
+ final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
+ Intent intent) -> {
+ mMetricsLogger.action(MetricsEvent.ACTION_APP_NOTE_SETTINGS);
+ guts.resetFalsingCheck();
+ startNotificationGutsIntent(intent, sbn.getUid());
+ };
final View.OnClickListener onDoneClick = (View v) -> {
saveAndCloseNotificationMenu(info, row, guts, v);
};
- final NotificationInfo.CheckSaveListener checkSaveListener = (Runnable saveImportance) -> {
+ final NotificationInfo.CheckSaveListener checkSaveListener =
+ (Runnable saveImportance) -> {
// If the user has security enabled, show challenge if the setting is changed.
if (isLockscreenPublicMode(userHandle.getIdentifier())
&& (mState == StatusBarState.KEYGUARD
@@ -5817,7 +5826,9 @@
}
try {
info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
- onSettingsClick, onDoneClick, checkSaveListener, mNonBlockablePkgs);
+ row.getEntry().channel.getImportance(), sbn, onSettingsClick,
+ onAppSettingsClick, onDoneClick, checkSaveListener,
+ mNonBlockablePkgs);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 0621f4a..2bb7f3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -18,9 +18,9 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
@@ -33,13 +33,18 @@
import static org.mockito.Mockito.when;
import android.app.INotificationManager;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -56,6 +61,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -74,6 +81,7 @@
private final PackageManager mMockPackageManager = mock(PackageManager.class);
private NotificationChannel mNotificationChannel;
private NotificationChannel mDefaultNotificationChannel;
+ private StatusBarNotification mSbn;
@Before
public void setUp() throws Exception {
@@ -101,6 +109,8 @@
mDefaultNotificationChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
NotificationManager.IMPORTANCE_LOW);
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0,
+ new Notification(), UserHandle.CURRENT, null, 0);
}
private CharSequence getStringById(int resId) {
@@ -135,7 +145,9 @@
public void testBindNotification_SetsTextApplicationName() throws Exception {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.pkgname);
assertTrue(textView.getText().toString().contains("App Name"));
}
@@ -146,7 +158,9 @@
when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
.thenReturn(iconDrawable);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
final ImageView iconView = (ImageView) mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@@ -154,7 +168,9 @@
@Test
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.GONE, groupNameView.getVisibility());
final TextView groupDividerView =
@@ -171,7 +187,9 @@
eq("test_group_id"), eq(TEST_PACKAGE_NAME), anyInt()))
.thenReturn(notificationChannelGroup);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -183,7 +201,9 @@
@Test
public void testBindNotification_SetsTextChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -193,10 +213,11 @@
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn,
(View v, NotificationChannel c, int appUid) -> {
- assertEquals(mNotificationChannel, c);
- latch.countDown();
- }, null, null, null);
+ assertEquals(mNotificationChannel, c);
+ latch.countDown();
+ }, null, null, null, null);
final TextView settingsButton =
(TextView) mNotificationInfo.findViewById(R.id.more_settings);
@@ -209,7 +230,7 @@
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- null, null, null, null);
+ mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
final TextView settingsButton =
(TextView) mNotificationInfo.findViewById(R.id.more_settings);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -219,10 +240,11 @@
public void testBindNotification_SettingsButtonReappersAfterSecondBind() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- null, null, null, null);
+ mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- (View v, NotificationChannel c, int appUid) -> {}, null, null, null);
+ mNotificationChannel.getImportance(), mSbn,
+ (View v, NotificationChannel c, int appUid) -> {}, null, null, null, null);
final TextView settingsButton =
(TextView) mNotificationInfo.findViewById(R.id.more_settings);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
@@ -234,10 +256,11 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME,
Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn,
(View v, NotificationChannel c, int appUid) -> {
- assertEquals(null, c);
- latch.countDown();
- }, null, null, null);
+ assertEquals(null, c);
+ latch.countDown();
+ }, null, null, null, null);
final TextView settingsButton =
(TextView) mNotificationInfo.findViewById(R.id.more_settings);
@@ -250,7 +273,9 @@
public void testBindNotification_SettingsTextWithOneChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- (View v, NotificationChannel c, int appUid) -> {}, null, null, null);
+ mNotificationChannel.getImportance(), mSbn,
+ (View v, NotificationChannel c, int appUid) -> {
+ }, null, null, null, null);
final TextView settingsButton =
(TextView) mNotificationInfo.findViewById(R.id.more_settings);
assertEquals(getStringById(R.string.notification_more_settings), settingsButton.getText());
@@ -262,7 +287,9 @@
eq(TEST_PACKAGE_NAME), anyInt(), anyBoolean())).thenReturn(2);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- (View v, NotificationChannel c, int appUid) -> {}, null, null, null);
+ mNotificationChannel.getImportance(), mSbn,
+ (View v, NotificationChannel c, int appUid) -> {
+ }, null, null, null, null);
final TextView settingsButton =
(TextView) mNotificationInfo.findViewById(R.id.more_settings);
assertEquals(getStringById(R.string.notification_all_categories), settingsButton.getText());
@@ -272,8 +299,11 @@
public void testBindNotification_SetsOnClickListenerForDone() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null,
- (View v) -> { latch.countDown(); },
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null,
+ null, (View v) -> {
+ latch.countDown();
+ },
null, null);
final TextView doneButton = (TextView) mNotificationInfo.findViewById(R.id.done);
@@ -286,7 +316,8 @@
public void testBindNotification_NumChannelsTextUniqueWhenDefaultChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel),
- null, null, null, null);
+ mNotificationChannel.getImportance(), mSbn, null, null,
+ null, null, null);
final TextView numChannelsView =
(TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
assertEquals(View.VISIBLE, numChannelsView.getVisibility());
@@ -298,7 +329,9 @@
public void testBindNotification_NumChannelsTextDisplaysWhenNotDefaultChannel()
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
final TextView numChannelsView =
(TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
assertEquals(numChannelsView.getVisibility(), View.VISIBLE);
@@ -311,7 +344,9 @@
when(mMockINotificationManager.getNumNotificationChannelsForPackage(
eq(TEST_PACKAGE_NAME), anyInt(), anyBoolean())).thenReturn(2);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
final TextView numChannelsView =
(TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
assertEquals(getNumChannelsDescString(2), numChannelsView.getText());
@@ -323,7 +358,7 @@
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
- null, null, null, null);
+ mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
final TextView numChannelsView =
(TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
assertEquals(getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel),
@@ -339,7 +374,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME,
Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel),
- null, null, null, null);
+ mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
final TextView numChannelsView =
(TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
assertEquals(
@@ -359,8 +394,8 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME,
Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel,
- fourthChannel),
- null, null, null, null);
+ fourthChannel), mNotificationChannel.getImportance(), mSbn, null, null,
+ null, null, null);
final TextView numChannelsView =
(TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
assertEquals(
@@ -375,7 +410,7 @@
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
- null, null, null, null);
+ mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
final TextView channelNameView =
(TextView) mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(getNumChannelsString(2), channelNameView.getText());
@@ -386,7 +421,7 @@
public void testEnabledSwitchInvisibleIfBundleFromDifferentChannels() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
- null, null, null, null);
+ mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
}
@@ -394,7 +429,8 @@
@Test
public void testbindNotification_ChannelDisabledTextGoneWhenNotDisabled() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
final TextView channelDisabledView =
(TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
assertEquals(channelDisabledView.getVisibility(), View.GONE);
@@ -404,7 +440,9 @@
public void testbindNotification_ChannelDisabledTextVisibleWhenDisabled() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
final TextView channelDisabledView =
(TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
assertEquals(channelDisabledView.getVisibility(), View.VISIBLE);
@@ -421,7 +459,8 @@
mDefaultNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel),
- null, null, null, null);
+ mDefaultNotificationChannel.getImportance(), mSbn, null, null,
+ null, null, null);
final TextView channelDisabledView =
(TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
assertEquals(View.VISIBLE, channelDisabledView.getVisibility());
@@ -430,7 +469,9 @@
@Test
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), anyInt(), any());
}
@@ -439,7 +480,9 @@
public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
enabledSwitch.setChecked(false);
@@ -451,7 +494,9 @@
public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged()
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
mNotificationInfo.handleCloseControls(true);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -463,7 +508,9 @@
throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
mNotificationInfo.handleCloseControls(true);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -474,7 +521,9 @@
public void testEnabledSwitchOnByDefault() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
assertTrue(enabledSwitch.isChecked());
@@ -484,7 +533,9 @@
public void testEnabledButtonOffWhenAlreadyBanned() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
assertFalse(enabledSwitch.isChecked());
@@ -494,7 +545,9 @@
public void testEnabledSwitchVisibleByDefault() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, null);
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
assertEquals(View.VISIBLE, enabledSwitch.getVisibility());
@@ -504,8 +557,9 @@
public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null,
- Collections.singleton(TEST_PACKAGE_NAME));
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, Collections.singleton(TEST_PACKAGE_NAME));
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
@@ -515,8 +569,9 @@
public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null,
- Collections.singleton(TEST_PACKAGE_NAME));
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, Collections.singleton(TEST_PACKAGE_NAME));
mNotificationInfo.handleCloseControls(true);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), anyInt(), any());
@@ -526,8 +581,9 @@
public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null,
- Collections.singleton(TEST_PACKAGE_NAME));
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, Collections.singleton(TEST_PACKAGE_NAME));
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
enabledSwitch.setChecked(false);
@@ -540,8 +596,9 @@
public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null,
- Collections.singleton(TEST_PACKAGE_NAME));
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ null, Collections.singleton(TEST_PACKAGE_NAME));
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
enabledSwitch.setChecked(false);
@@ -554,8 +611,10 @@
public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null,
- (Runnable saveImportance) -> {},
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ (Runnable saveImportance) -> {
+ },
Collections.singleton(TEST_PACKAGE_NAME));
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
@@ -569,8 +628,11 @@
public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null,
- (Runnable saveImportance) -> { saveImportance.run(); },
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), mSbn, null, null, null,
+ (Runnable saveImportance) -> {
+ saveImportance.run();
+ },
Collections.singleton(TEST_PACKAGE_NAME));
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
@@ -579,4 +641,137 @@
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel));
}
+
+ @Test
+ public void testDisplaySettingsLink() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final String settingsText = "work chats";
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+ ri.activityInfo.name = "something";
+ List<ResolveInfo> ris = new ArrayList<>();
+ ris.add(ri);
+ when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+ mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+ .setSettingsText(settingsText).build();
+ StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+ 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), sbn, null,
+ (View v, Intent intent) -> {
+ latch.countDown();
+ }, null, null, null);
+ final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+ assertEquals(View.VISIBLE, settingsLink.getVisibility());
+ assertTrue(settingsLink.getText().toString().length() > settingsText.length());
+ assertTrue(settingsLink.getText().toString().contains(settingsText));
+ settingsLink.performClick();
+ assertEquals(0, latch.getCount());
+ }
+
+ @Test
+ public void testDisplaySettingsLink_multipleChannels() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final String settingsText = "work chats";
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+ ri.activityInfo.name = "something";
+ List<ResolveInfo> ris = new ArrayList<>();
+ ris.add(ri);
+ when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+ mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+ .setSettingsText(settingsText).build();
+ StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+ 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
+ mNotificationChannel.getImportance(), sbn, null, (View v, Intent intent) -> {
+ latch.countDown();
+ }, null, null, null);
+ final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+ assertEquals(View.VISIBLE, settingsLink.getVisibility());
+ settingsLink.performClick();
+ assertEquals(0, latch.getCount());
+ }
+
+ @Test
+ public void testNoSettingsLink_noHandlingActivity() throws Exception {
+ final String settingsText = "work chats";
+ when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null);
+ mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+ .setSettingsText(settingsText).build();
+ StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+ 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), sbn, null, null, null,
+ null, null);
+ final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+ assertEquals(View.GONE, settingsLink.getVisibility());
+ }
+
+ @Test
+ public void testNoSettingsLink_noLinkText() throws Exception {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+ ri.activityInfo.name = "something";
+ List<ResolveInfo> ris = new ArrayList<>();
+ ris.add(ri);
+ when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+ mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build();
+ StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+ 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), sbn, null, null, null,
+ null, null);
+ final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+ assertEquals(View.GONE, settingsLink.getVisibility());
+ }
+
+ @Test
+ public void testNoSettingsLink_afterBlockingChannel() throws Exception {
+ final String settingsText = "work chats";
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+ ri.activityInfo.name = "something";
+ List<ResolveInfo> ris = new ArrayList<>();
+ ris.add(ri);
+ when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+ mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+ .setSettingsText(settingsText).build();
+ StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+ 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+ mNotificationChannel.getImportance(), sbn, null, null, null,
+ null, null);
+ final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+ assertEquals(View.VISIBLE, settingsLink.getVisibility());
+
+ // Block channel
+ Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
+ enabledSwitch.setChecked(false);
+
+ assertEquals(View.GONE, settingsLink.getVisibility());
+
+ //unblock
+ enabledSwitch.setChecked(true);
+ assertEquals(View.VISIBLE, settingsLink.getVisibility());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
index a69de7a..2c49f99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.ExpandableNotificationRow;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,6 +38,7 @@
mContext = InstrumentationRegistry.getTargetContext();
}
+ @Ignore("Broken")
@Test
public void constructor_doesntUseViewContext() throws Exception {
new TestableNotificationViewWrapper(mContext, new View(null /* context */), null /* row */);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index f8b8e76..c8a5780 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -148,7 +148,7 @@
// TODO(b/33197203): since service is fetching the data (to use for save later),
// we should optimize what's sent (for example, remove layout containers,
// color / font info, etc...)
- session.mStructure = structure;
+ session.setStructureLocked(structure);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b02db18..906f81c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -29,6 +29,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.AutofillOverlay;
import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.WindowNode;
import android.content.ComponentName;
@@ -101,7 +102,7 @@
@NonNull private final String mPackageName;
@GuardedBy("mLock")
- private final Map<AutofillId, ViewState> mViewStates = new ArrayMap<>();
+ private final ArrayMap<AutofillId, ViewState> mViewStates = new ArrayMap<>();
/**
* Id of the View currently being displayed.
@@ -133,7 +134,8 @@
* Assist structure sent by the app; it will be updated (sanitized, change values for save)
* before sent to {@link AutofillService}.
*/
- @GuardedBy("mLock") AssistStructure mStructure;
+ @GuardedBy("mLock")
+ private AssistStructure mStructure;
/**
* Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}.
@@ -141,6 +143,13 @@
private boolean mHasCallback;
/**
+ * Extras sent by service on {@code onFillRequest()} calls; the first non-null extra is saved
+ * and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls.
+ */
+ @GuardedBy("mLock")
+ private Bundle mExtras;
+
+ /**
* Flags used to start the session.
*/
int mFlags;
@@ -306,12 +315,22 @@
@Override
public void requestShowFillUi(AutofillId id, int width, int height,
IAutofillWindowPresenter presenter) {
- try {
- final ViewState currentView = mViewStates.get(mCurrentViewId);
- mClient.requestShowFillUi(mWindowToken, id, width, height,
- currentView.getVirtualBounds(), presenter);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error requesting to show fill UI", e);
+ synchronized (mLock) {
+ if (id.equals(mCurrentViewId)) {
+ try {
+ final ViewState view = mViewStates.get(id);
+ mClient.requestShowFillUi(mWindowToken, id, width, height,
+ view.getVirtualBounds(),
+ presenter);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error requesting to show fill UI", e);
+ }
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Do not show full UI on " + id + " as it is not the current view ("
+ + mCurrentViewId + ") anymore");
+ }
+ }
}
}
@@ -356,6 +375,10 @@
mHasCallback = hasIt;
}
+ public void setStructureLocked(AssistStructure structure) {
+ mStructure = structure;
+ }
+
/**
* Shows the save UI, when session can be saved.
*
@@ -478,9 +501,6 @@
Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates);
}
- // TODO(b/33197203 , b/35707731): decide how to handle bundle in multiple partitions
- final Bundle extras = mResponses != null ? mResponses.get(0).getExtras() : null;
-
for (Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
final AutofillValue value = entry.getValue().getCurrentValue();
if (value == null) {
@@ -510,7 +530,7 @@
mStructure.dump();
}
- mRemoteFillService.onSaveRequest(mStructure, extras);
+ mRemoteFillService.onSaveRequest(mStructure, mExtras);
}
void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) {
@@ -521,10 +541,10 @@
if (DEBUG) {
Slog.d(TAG, "Creating viewState for " + id + " on " + getFlagAsString(flags));
}
- viewState = new ViewState(this, id, this, ViewState.STATE_INITIAL);
+ viewState = new ViewState(this, id, value, this, ViewState.STATE_INITIAL);
mViewStates.put(id, viewState);
} else if ((flags & FLAG_VIEW_ENTERED) != 0) {
- viewState = startPartitionLocked(id);
+ viewState = startPartitionLocked(id, value);
} else {
if (VERBOSE) Slog.v(TAG, "Ignored " + getFlagAsString(flags) + " for " + id);
return;
@@ -588,25 +608,38 @@
Slog.w(TAG, "updateLocked(): unknown flags " + flags + ": " + getFlagAsString(flags));
}
- private ViewState startPartitionLocked(AutofillId id) {
+ private ViewState startPartitionLocked(AutofillId id, AutofillValue value) {
if (DEBUG) {
Slog.d(TAG, "Starting partition for view id " + id);
}
- final ViewState viewState =
- new ViewState(this, id, this,ViewState.STATE_STARTED_PARTITION);
- mViewStates.put(id, viewState);
+ final ViewState newViewState =
+ new ViewState(this, id, value, this,ViewState.STATE_STARTED_PARTITION);
+ mViewStates.put(id, newViewState);
- /*
- * TODO(b/33197203 , b/35707731): when start a new partition, it should
- *
- * - add autofilled fields as sanitized
- * - set focus on ViewStructure that triggered it
- * - pass the first onFillRequest() bundle
- * - optional: perhaps add a new flag onFilLRequest() to indicate it's a new partition?
- */
- mRemoteFillService.onFillRequest(mStructure, null, 0);
+ // Must update value of nodes so:
+ // - proper node is focused
+ // - autofillValue is sent back to service when it was previously autofilled
+ for (int i = 0; i < mViewStates.size(); i++) {
+ final ViewState viewState = mViewStates.valueAt(i);
- return viewState;
+ final ViewNode node = findViewNodeByIdLocked(viewState.id);
+ if (node == null) {
+ Slog.w(TAG, "startPartitionLocked(): no node for " + viewState.id);
+ continue;
+ }
+
+ final AutofillValue initialValue = viewState.getInitialValue();
+ final AutofillValue filledValue = viewState.getAutofilledValue();
+ final AutofillOverlay overlay = new AutofillOverlay();
+ if (filledValue != null && !filledValue.equals(initialValue)) {
+ overlay.value = filledValue;
+ }
+ overlay.focused = id.equals(viewState.id);
+ node.setAutofillOverlay(overlay);
+ }
+ mRemoteFillService.onFillRequest(mStructure, mExtras, 0);
+
+ return newViewState;
}
@Override
@@ -654,6 +687,9 @@
mResponses = new ArrayList<>(4);
}
mResponses.add(response);
+ if (response != null) {
+ mExtras = response.getExtras();
+ }
setViewStatesLocked(response, ViewState.STATE_FILLABLE);
@@ -699,7 +735,7 @@
if (viewState != null) {
viewState.setState(state);
} else {
- viewState = new ViewState(this, id, this, state);
+ viewState = new ViewState(this, id, null, this, state);
if (DEBUG) { // TODO(b/33197203): change to VERBOSE once stable
Slog.d(TAG, "Adding autofillable view with id " + id + " and state " + state);
}
@@ -795,6 +831,7 @@
}
}
pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
+ pw.print(prefix); pw.print("mExtras: "); pw.println(Helper.bundleToString(mExtras));
mRemoteFillService.dump(prefix, pw);
}
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 549f231..f8919ee 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -67,15 +67,17 @@
private final Session mSession;
private FillResponse mResponse;
+ private AutofillValue mInitialValue;
private AutofillValue mCurrentValue;
private AutofillValue mAutofilledValue;
private Rect mVirtualBounds;
private int mState;
- ViewState(Session session, AutofillId id, Listener listener, int state) {
+ ViewState(Session session, AutofillId id, AutofillValue value, Listener listener, int state) {
mSession = session;
this.id = id;
+ mInitialValue = value;
mListener = listener;
mState = state;
}
@@ -110,6 +112,11 @@
}
@Nullable
+ AutofillValue getInitialValue() {
+ return mInitialValue;
+ }
+
+ @Nullable
FillResponse getResponse() {
return mResponse;
}
@@ -184,14 +191,16 @@
@Override
public String toString() {
- return "ViewState: [id=" + id + ", currentValue=" + mCurrentValue
- + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() +"]";
+ return "ViewState: [id=" + id + ", initialValue=" + mInitialValue
+ + ", currentValue=" + mCurrentValue + ", autofilledValue=" + mAutofilledValue
+ + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() + "]";
}
void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("id:" ); pw.println(this.id);
pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString());
pw.print(prefix); pw.print("has response:" ); pw.println(mResponse != null);
+ pw.print(prefix); pw.print("initialValue:" ); pw.println(mInitialValue);
pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 25ac008..50c0a12 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1265,13 +1265,16 @@
@Override
public LinkProperties getLinkProperties(Network network) {
enforceAccessPermission();
- NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai != null) {
- synchronized (nai) {
- return new LinkProperties(nai.linkProperties);
- }
+ return getLinkProperties(getNetworkAgentInfoForNetwork(network));
+ }
+
+ private LinkProperties getLinkProperties(NetworkAgentInfo nai) {
+ if (nai == null) {
+ return null;
}
- return null;
+ synchronized (nai) {
+ return new LinkProperties(nai.linkProperties);
+ }
}
private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
@@ -3027,7 +3030,8 @@
enforceAccessPermission();
enforceInternetPermission();
- NetworkAgentInfo nai;
+ // TODO: execute this logic on ConnectivityService handler.
+ final NetworkAgentInfo nai;
if (network == null) {
nai = getDefaultNetwork();
} else {
@@ -3038,21 +3042,24 @@
return;
}
// Revalidate if the app report does not match our current validated state.
- if (hasConnectivity == nai.lastValidated) return;
+ if (hasConnectivity == nai.lastValidated) {
+ return;
+ }
final int uid = Binder.getCallingUid();
if (DBG) {
log("reportNetworkConnectivity(" + nai.network.netId + ", " + hasConnectivity +
") by " + uid);
}
- synchronized (nai) {
- // Validating a network that has not yet connected could result in a call to
- // rematchNetworkAndRequests() which is not meant to work on such networks.
- if (!nai.everConnected) return;
-
- if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, false)) return;
-
- nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
+ // Validating a network that has not yet connected could result in a call to
+ // rematchNetworkAndRequests() which is not meant to work on such networks.
+ if (!nai.everConnected) {
+ return;
}
+ LinkProperties lp = getLinkProperties(nai);
+ if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
+ return;
+ }
+ nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
}
private ProxyInfo getDefaultProxy() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ee2fdba..73aeee9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -333,7 +333,6 @@
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionSession;
-import android.service.vr.IPersistentVrStateCallbacks;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -664,70 +663,8 @@
// default action automatically. Important for devices without direct input
// devices.
private boolean mShowDialogs = true;
- // VR state flags.
- static final int NON_VR_MODE = 0;
- static final int VR_MODE = 1;
- static final int PERSISTENT_VR_MODE = 2;
- private int mVrState = NON_VR_MODE;
- private int mTopAppVrThreadTid = 0;
- private int mPersistentVrThreadTid = 0;
- final IPersistentVrStateCallbacks mPersistentVrModeListener =
- new IPersistentVrStateCallbacks.Stub() {
- @Override
- public void onPersistentVrStateChanged(boolean enabled) {
- synchronized(ActivityManagerService.this) {
- // There are 4 possible cases here:
- //
- // Cases for enabled == true
- // Invariant: mVrState != PERSISTENT_VR_MODE;
- // This is guaranteed as only this function sets mVrState to PERSISTENT_VR_MODE
- // Invariant: mPersistentVrThreadTid == 0
- // This is guaranteed by VrManagerService, which only emits callbacks when the
- // mode changes, and in setPersistentVrThread, which only sets
- // mPersistentVrThreadTid when mVrState = PERSISTENT_VR_MODE
- // Case 1: mTopAppVrThreadTid > 0 (someone called setVrThread successfully and is
- // the top-app)
- // We reset the app which currently has SCHED_FIFO (mPersistentVrThreadTid) to
- // SCHED_OTHER
- // Case 2: mTopAppVrThreadTid == 0
- // Do nothing
- //
- // Cases for enabled == false
- // Invariant: mVrState == PERSISTENT_VR_MODE;
- // This is guaranteed by VrManagerService, which only emits callbacks when the
- // mode changes, and the only other assignment of mVrState outside of this
- // function checks if mVrState != PERSISTENT_VR_MODE
- // Invariant: mTopAppVrThreadTid == 0
- // This is guaranteed in that mTopAppVrThreadTid is only set to a tid when
- // mVrState is VR_MODE, and is explicitly set to 0 when setPersistentVrThread is
- // called
- // mPersistentVrThreadTid > 0 (someone called setPersistentVrThread successfully)
- // 3. Reset mPersistentVrThreadTidto SCHED_OTHER
- // mPersistentVrThreadTid == 0
- // 4. Do nothing
- if (enabled) {
- mVrState = PERSISTENT_VR_MODE;
- } else {
- // Leaving persistent mode implies leaving VR mode.
- mVrState = NON_VR_MODE;
- }
- if (mVrState == PERSISTENT_VR_MODE) {
- if (mTopAppVrThreadTid > 0) {
- // Ensure that when entering persistent VR mode the last top-app loses
- // SCHED_FIFO.
- setThreadScheduler(mTopAppVrThreadTid, SCHED_OTHER, 0);
- mTopAppVrThreadTid = 0;
- }
- } else if (mPersistentVrThreadTid > 0) {
- // Ensure that when leaving persistent VR mode we reschedule the high priority
- // persistent thread.
- setThreadScheduler(mPersistentVrThreadTid, SCHED_OTHER, 0);
- mPersistentVrThreadTid = 0;
- }
- }
- }
- };
+ private final VrController mVrController;
// VR Compatibility Display Id.
int mVrCompatibilityDisplayId = INVALID_DISPLAY;
@@ -2447,53 +2384,19 @@
idleUids();
} break;
case VR_MODE_CHANGE_MSG: {
- VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
- if (vrService == null) {
- break;
- }
- final ActivityRecord r = (ActivityRecord) msg.obj;
- boolean vrMode;
- boolean inVrMode;
- ComponentName requestedPackage;
- ComponentName callingPackage;
- int userId;
- synchronized (ActivityManagerService.this) {
- vrMode = r.requestedVrComponent != null;
- inVrMode = mVrState != NON_VR_MODE;
- requestedPackage = r.requestedVrComponent;
- userId = r.userId;
- callingPackage = r.info.getComponentName();
- if (vrMode != inVrMode) {
- // Don't change state if we're in persistent VR mode, but do update thread
- // priorities if necessary.
- if (mVrState != PERSISTENT_VR_MODE) {
- mVrState = vrMode ? VR_MODE : NON_VR_MODE;
- }
- mShowDialogs = shouldShowDialogs(getGlobalConfiguration(), vrMode);
- if (r.app != null) {
- ProcessRecord proc = r.app;
- if (proc.vrThreadTid > 0) {
- if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
- try {
- if (mVrState == VR_MODE) {
- setThreadScheduler(proc.vrThreadTid,
- SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
- mTopAppVrThreadTid = proc.vrThreadTid;
- } else {
- setThreadScheduler(proc.vrThreadTid,
- SCHED_OTHER, 0);
- mTopAppVrThreadTid = 0;
- }
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed to set scheduling policy, thread does"
- + " not exist:\n" + e);
- }
- }
+ if (mVrController.onVrModeChanged((ActivityRecord) msg.obj)) {
+ synchronized (ActivityManagerService.this) {
+ if (mVrController.shouldDisableNonVrUiLocked()) {
+ // If we are in a VR mode where Picture-in-Picture mode is unsupported,
+ // then remove the pinned stack.
+ final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
+ PINNED_STACK_ID);
+ if (pinnedStack != null) {
+ mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
}
}
}
}
- vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
} break;
case NOTIFY_VR_SLEEPING_MSG: {
notifyVrManagerOfSleepState(msg.arg1 != 0);
@@ -2770,6 +2673,7 @@
mTaskChangeNotificationController = null;
mUiHandler = injector.getUiHandler(null);
mUserController = null;
+ mVrController = null;
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2856,6 +2760,8 @@
mUserController = new UserController(this);
+ mVrController = new VrController(this);
+
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -13263,23 +13169,12 @@
@Override
public void setVrThread(int tid) {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
-
+ enforceSystemHasVrFeature();
synchronized (this) {
- if (tid > 0 && mVrState == PERSISTENT_VR_MODE) {
- Slog.e(TAG, "VR thread cannot be set in persistent VR mode!");
- return;
- }
- ProcessRecord proc;
synchronized (mPidsSelfLocked) {
final int pid = Binder.getCallingPid();
- proc = mPidsSelfLocked.get(pid);
- if (proc != null && mVrState == VR_MODE && tid >= 0) {
- proc.vrThreadTid = updateVrThreadLocked(proc, proc.vrThreadTid, pid, tid);
- mTopAppVrThreadTid = proc.vrThreadTid;
- }
+ final ProcessRecord proc = mPidsSelfLocked.get(pid);
+ mVrController.setVrThreadLocked(tid, pid, proc);
}
}
}
@@ -13287,72 +13182,71 @@
@Override
public void setPersistentVrThread(int tid) {
if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) {
- String msg = "Permission Denial: setPersistentVrThread() from pid="
+ final String msg = "Permission Denial: setPersistentVrThread() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + permission.RESTRICTED_VR_ACCESS;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
-
+ enforceSystemHasVrFeature();
synchronized (this) {
- // Disable any existing VR thread.
- if (mTopAppVrThreadTid > 0) {
- setThreadScheduler(mTopAppVrThreadTid, SCHED_OTHER, 0);
- mTopAppVrThreadTid = 0;
- }
-
- if (tid > 0 && mVrState != PERSISTENT_VR_MODE) {
- Slog.e(TAG, "Persistent VR thread may only be set in persistent VR mode!");
- return;
- }
- ProcessRecord proc;
synchronized (mPidsSelfLocked) {
final int pid = Binder.getCallingPid();
- mPersistentVrThreadTid =
- updateVrThreadLocked(null, mPersistentVrThreadTid, pid, tid);
+ final ProcessRecord proc = mPidsSelfLocked.get(pid);
+ mVrController.setPersistentVrThreadLocked(tid, pid, proc);
}
}
}
/**
- * Used by setVrThread and setPersistentVrThread to update a thread's priority. When proc is
- * non-null it must be in SCHED_GROUP_TOP_APP. When it is null, the tid is unconditionally
- * rescheduled.
+ * Schedule the given thread a normal scheduling priority.
+ *
+ * @param newTid the tid of the thread to adjust the scheduling of.
+ * @param suppressLogs {@code true} if any error logging should be disabled.
+ *
+ * @return {@code true} if this succeeded.
*/
- private int updateVrThreadLocked(ProcessRecord proc, int lastTid, int pid, int tid) {
- // ensure the tid belongs to the process
- if (!isThreadInProcess(pid, tid)) {
- throw new IllegalArgumentException("VR thread does not belong to process");
- }
-
- // reset existing VR thread to CFS if this thread still exists and belongs to
- // the calling process
- if (lastTid != 0 && isThreadInProcess(pid, lastTid)) {
- try {
- setThreadScheduler(lastTid, SCHED_OTHER, 0);
- } catch (IllegalArgumentException e) {
- // Ignore this. Only occurs in race condition where previous VR thread
- // was destroyed during this method call.
- }
- }
-
- // promote to FIFO now if the tid is non-zero
+ static boolean scheduleAsRegularPriority(int tid, boolean suppressLogs) {
try {
- if ((proc == null || proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP)
- && tid > 0) {
- setThreadScheduler(tid,
- SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
- }
- return tid;
+ Process.setThreadScheduler(tid, Process.SCHED_OTHER, 0);
+ return true;
} catch (IllegalArgumentException e) {
- Slog.e(TAG, "Failed to set scheduling policy, thread does"
- + " not exist:\n" + e);
+ if (!suppressLogs) {
+ Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e);
+ }
}
- return lastTid;
+ return false;
+ }
+
+ /**
+ * Schedule the given thread an FIFO scheduling priority.
+ *
+ * @param newTid the tid of the thread to adjust the scheduling of.
+ * @param suppressLogs {@code true} if any error logging should be disabled.
+ *
+ * @return {@code true} if this succeeded.
+ */
+ static boolean scheduleAsFifoPriority(int tid, boolean suppressLogs) {
+ try {
+ Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ return true;
+ } catch (IllegalArgumentException e) {
+ if (!suppressLogs) {
+ Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check that we have the features required for VR-related API calls, and throw an exception if
+ * not.
+ */
+ private void enforceSystemHasVrFeature() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+ throw new UnsupportedOperationException("VR mode not supported on this device!");
+ }
}
@Override
@@ -13447,6 +13341,13 @@
}
}
+ /**
+ * @return whether the system should disable UI modes incompatible with VR mode.
+ */
+ boolean shouldDisableNonVrUiLocked() {
+ return mVrController.shouldDisableNonVrUiLocked();
+ }
+
@Override
public boolean isTopOfTask(IBinder token) {
synchronized (this) {
@@ -13944,10 +13845,7 @@
mLocalDeviceIdleController
= LocalServices.getService(DeviceIdleController.LocalService.class);
mAssistUtils = new AssistUtils(mContext);
- VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class);
- if (vrManagerInternal != null) {
- vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
- }
+ mVrController.onSystemReady();
// Make sure we have the current profile info, since it is needed for security checks.
mUserController.onSystemReady();
mRecentTasks.onSystemReadyLocked();
@@ -15629,6 +15527,7 @@
pw.println(" mVoiceWakeLock" + mVoiceWakeLock);
}
}
+ pw.println(" mVrController=" + mVrController);
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
if (dumpPackage == null || dumpPackage.equals(mDebugApp)
@@ -20055,7 +19954,7 @@
mUserController.getCurrentUserIdLocked());
// TODO: If our config changes, should we auto dismiss any currently showing dialogs?
- mShowDialogs = shouldShowDialogs(mTempConfig, mVrState != NON_VR_MODE);
+ mShowDialogs = shouldShowDialogs(mTempConfig);
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
@@ -20285,15 +20184,16 @@
* A thought: SystemUI might also want to get told about this, the Power
* dialog / global actions also might want different behaviors.
*/
- private static boolean shouldShowDialogs(Configuration config, boolean inVrMode) {
+ private static boolean shouldShowDialogs(Configuration config) {
final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
&& config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
&& config.navigation == Configuration.NAVIGATION_NONAV);
int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
&& !(modeType == Configuration.UI_MODE_TYPE_WATCH && "user".equals(Build.TYPE))
- && modeType != Configuration.UI_MODE_TYPE_TELEVISION);
- return inputMethodExists && uiModeSupportsDialogs && !inVrMode;
+ && modeType != Configuration.UI_MODE_TYPE_TELEVISION
+ && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
+ return inputMethodExists && uiModeSupportsDialogs;
}
@Override
@@ -21596,32 +21496,14 @@
if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
- // Switch VR thread for app to SCHED_FIFO
- if (mVrState == VR_MODE && app.vrThreadTid != 0) {
- try {
- setThreadScheduler(app.vrThreadTid,
- SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
- mTopAppVrThreadTid = app.vrThreadTid;
- } catch (IllegalArgumentException e) {
- // thread died, ignore
- }
- }
+ mVrController.onTopProcChangedLocked(app);
if (mUseFifoUiScheduling) {
// Switch UI pipeline for app to SCHED_FIFO
- app.savedPriority = getThreadPriority(app.pid);
- try {
- setThreadScheduler(app.pid,
- SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
- } catch (IllegalArgumentException e) {
- // thread died, ignore
- }
+ app.savedPriority = Process.getThreadPriority(app.pid);
+ scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
if (app.renderThreadTid != 0) {
- try {
- setThreadScheduler(app.renderThreadTid,
- SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
- } catch (IllegalArgumentException e) {
- // thread died, ignore
- }
+ scheduleAsFifoPriority(app.renderThreadTid,
+ /* suppressLogs */true);
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Set RenderThread (TID " +
app.renderThreadTid + ") to FIFO");
@@ -21645,12 +21527,7 @@
}
} else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
- // Reset VR thread to SCHED_OTHER
- // Safe to do even if we're not in VR mode
- if (app.vrThreadTid != 0) {
- setThreadScheduler(app.vrThreadTid, SCHED_OTHER, 0);
- mTopAppVrThreadTid = 0;
- }
+ mVrController.onTopProcChangedLocked(app);
if (mUseFifoUiScheduling) {
// Reset UI pipeline to SCHED_OTHER
setThreadScheduler(app.pid, SCHED_OTHER, 0);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 3a29414..6e7ad17 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1164,6 +1164,11 @@
return false;
}
+ // Check to see if we are in VR mode, and disallow PiP if so
+ if (service.shouldDisableNonVrUiLocked()) {
+ return false;
+ }
+
boolean isCurrentAppLocked = mStackSupervisor.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
boolean isKeyguardLocked = service.isKeyguardLocked();
boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index e5b2eca..983c975 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1021,32 +1021,24 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- Slog.d(TAG, "begin setBatteryStateLocked");
- try {
- synchronized (mStats) {
- final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
- if (mStats.isOnBattery() == onBattery) {
- // The battery state has not changed, so we don't need to sync external
- // stats immediately.
- mStats.setBatteryStateLocked(status, health, plugType, level, temp,
- volt,
- chargeUAh, chargeFullUAh);
- return;
- }
+ synchronized (mStats) {
+ final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
+ if (mStats.isOnBattery() == onBattery) {
+ // The battery state has not changed, so we don't need to sync external
+ // stats immediately.
+ mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
+ chargeUAh, chargeFullUAh);
+ return;
}
- } finally {
- Slog.d(TAG, "end setBatteryStateLocked");
}
// Sync external stats first as the battery has changed states. If we don't sync
// immediately here, we may not collect the relevant data later.
updateExternalStatsSync("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
- Slog.d(TAG, "begin setBatteryStateLocked");
synchronized (mStats) {
mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
chargeUAh, chargeFullUAh);
}
- Slog.d(TAG, "end setBatteryStateLocked");
}
});
}
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 73a17c6..160c753 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -21,6 +21,9 @@
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.HashMap;
import java.util.Map;
@@ -34,11 +37,14 @@
private static final String LOG_TAG = CoreSettingsObserver.class.getSimpleName();
// mapping form property name to its type
- private static final Map<String, Class<?>> sSecureSettingToTypeMap = new HashMap<
+ @VisibleForTesting
+ static final Map<String, Class<?>> sSecureSettingToTypeMap = new HashMap<
String, Class<?>>();
- private static final Map<String, Class<?>> sSystemSettingToTypeMap = new HashMap<
+ @VisibleForTesting
+ static final Map<String, Class<?>> sSystemSettingToTypeMap = new HashMap<
String, Class<?>>();
- private static final Map<String, Class<?>> sGlobalSettingToTypeMap = new HashMap<
+ @VisibleForTesting
+ static final Map<String, Class<?>> sGlobalSettingToTypeMap = new HashMap<
String, Class<?>>();
static {
sSecureSettingToTypeMap.put(Settings.Secure.LONG_PRESS_TIMEOUT, int.class);
@@ -101,51 +107,31 @@
}
}
- private void populateSettings(Bundle snapshot, Map<String, Class<?>> map) {
+ @VisibleForTesting
+ void populateSettings(Bundle snapshot, Map<String, Class<?>> map) {
Context context = mActivityManagerService.mContext;
for (Map.Entry<String, Class<?>> entry : map.entrySet()) {
String setting = entry.getKey();
+ final String value;
+ if (map == sSecureSettingToTypeMap) {
+ value = Settings.Secure.getString(context.getContentResolver(), setting);
+ } else if (map == sSystemSettingToTypeMap) {
+ value = Settings.System.getString(context.getContentResolver(), setting);
+ } else {
+ value = Settings.Global.getString(context.getContentResolver(), setting);
+ }
+ if (value == null) {
+ continue;
+ }
Class<?> type = entry.getValue();
if (type == String.class) {
- final String value;
- if (map == sSecureSettingToTypeMap) {
- value = Settings.Secure.getString(context.getContentResolver(), setting);
- } else if (map == sSystemSettingToTypeMap) {
- value = Settings.System.getString(context.getContentResolver(), setting);
- } else {
- value = Settings.Global.getString(context.getContentResolver(), setting);
- }
snapshot.putString(setting, value);
} else if (type == int.class) {
- final int value;
- if (map == sSecureSettingToTypeMap) {
- value = Settings.Secure.getInt(context.getContentResolver(), setting, 0);
- } else if (map == sSystemSettingToTypeMap) {
- value = Settings.System.getInt(context.getContentResolver(), setting, 0);
- } else {
- value = Settings.Global.getInt(context.getContentResolver(), setting, 0);
- }
- snapshot.putInt(setting, value);
+ snapshot.putInt(setting, Integer.parseInt(value));
} else if (type == float.class) {
- final float value;
- if (map == sSecureSettingToTypeMap) {
- value = Settings.Secure.getFloat(context.getContentResolver(), setting, 0);
- } else if (map == sSystemSettingToTypeMap) {
- value = Settings.System.getFloat(context.getContentResolver(), setting, 0);
- } else {
- value = Settings.Global.getFloat(context.getContentResolver(), setting, 0);
- }
- snapshot.putFloat(setting, value);
+ snapshot.putFloat(setting, Float.parseFloat(value));
} else if (type == long.class) {
- final long value;
- if (map == sSecureSettingToTypeMap) {
- value = Settings.Secure.getLong(context.getContentResolver(), setting, 0);
- } else if (map == sSystemSettingToTypeMap) {
- value = Settings.System.getLong(context.getContentResolver(), setting, 0);
- } else {
- value = Settings.Global.getLong(context.getContentResolver(), setting, 0);
- }
- snapshot.putLong(setting, value);
+ snapshot.putLong(setting, Long.parseLong(value));
}
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c7f20b9f..0c2c204 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1613,9 +1613,6 @@
String iconFilename = null;
int colorPrimary = 0;
int colorBackground = 0;
- int statusBarColor = 0;
- int navigationBarColor = 0;
- boolean topActivity = true;
for (--activityNdx; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
if (r.taskDescription != null) {
@@ -1628,16 +1625,13 @@
if (colorPrimary == 0) {
colorPrimary = r.taskDescription.getPrimaryColor();
}
- if (topActivity) {
+ if (colorBackground == 0) {
colorBackground = r.taskDescription.getBackgroundColor();
- statusBarColor = r.taskDescription.getStatusBarColor();
- navigationBarColor = r.taskDescription.getNavigationBarColor();
}
}
- topActivity = false;
}
lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
- colorBackground, statusBarColor, navigationBarColor);
+ colorBackground);
if (mWindowContainerController != null) {
mWindowContainerController.setTaskDescription(lastTaskDescription);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 728476a..3b5e5bc 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -670,7 +670,7 @@
}
mInjector.systemServiceManagerCleanupUser(userId);
synchronized (mLock) {
- mInjector.stackSupervisorRemoveUserLocked(userId);
+ mInjector.getActivityStackSupervisor().removeUserLocked(userId);
}
// Remove the user if it is ephemeral.
if (getUserInfo(userId).isEphemeral()) {
@@ -823,8 +823,10 @@
return true;
}
- mInjector.stackSupervisorSetLockTaskModeLocked(null,
- ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false);
+ if (foreground) {
+ mInjector.getActivityStackSupervisor().setLockTaskModeLocked(
+ null, ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false);
+ }
final UserInfo userInfo = getUserInfo(userId);
if (userInfo == null) {
@@ -1202,11 +1204,12 @@
}
void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) {
- boolean homeInFront = mInjector.stackSupervisorSwitchUserLocked(newUserId, uss);
+ boolean homeInFront =
+ mInjector.getActivityStackSupervisor().switchUserLocked(newUserId, uss);
if (homeInFront) {
mInjector.startHomeActivityLocked(newUserId, "moveUserToForeground");
} else {
- mInjector.stackSupervisorResumeFocusedStackTopActivityLocked();
+ mInjector.getActivityStackSupervisor().resumeFocusedStackTopActivityLocked();
}
EventLogTags.writeAmSwitchUser(newUserId);
sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
@@ -1680,10 +1683,6 @@
mService.mSystemServiceManager.cleanupUser(userId);
}
- void stackSupervisorRemoveUserLocked(int userId) {
- mService.mStackSupervisor.removeUserLocked(userId);
- }
-
protected UserManagerService getUserManager() {
if (mUserManager == null) {
IBinder b = ServiceManager.getService(Context.USER_SERVICE);
@@ -1743,24 +1742,10 @@
return mService.checkComponentPermission(permission, pid, uid, owningUid, exported);
}
- boolean stackSupervisorSwitchUserLocked(int userId, UserState uss) {
- return mService.mStackSupervisor.switchUserLocked(userId, uss);
- }
-
void startHomeActivityLocked(int userId, String reason) {
mService.startHomeActivityLocked(userId, reason);
}
- void stackSupervisorResumeFocusedStackTopActivityLocked() {
- mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
- }
-
- void stackSupervisorSetLockTaskModeLocked(TaskRecord task, int lockTaskModeState,
- String reason, boolean andResume) {
- mService.mStackSupervisor.setLockTaskModeLocked(task, lockTaskModeState, reason,
- andResume);
- }
-
void updateUserConfigurationLocked() {
mService.updateUserConfigurationLocked();
}
@@ -1778,5 +1763,9 @@
true /* above system */);
d.show();
}
+
+ ActivityStackSupervisor getActivityStackSupervisor() {
+ return mService.mStackSupervisor;
+ }
}
}
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
new file mode 100644
index 0000000..048bef7
--- /dev/null
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2017 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.am;
+
+import android.content.ComponentName;
+import android.os.Process;
+import android.service.vr.IPersistentVrStateCallbacks;
+import android.util.Slog;
+import com.android.server.LocalServices;
+import com.android.server.vr.VrManagerInternal;
+
+/**
+ * Helper class for {@link ActivityManagerService} responsible for VrMode-related ActivityManager
+ * functionality.
+ *
+ * <p>Specifically, this class is responsible for:
+ * <ul>
+ * <li>Adjusting the scheduling of VR render threads while in VR mode.
+ * <li>Handling ActivityManager calls to set a VR or a 'persistent' VR thread.
+ * <li>Tracking the state of ActivityManagerService's view of VR-related behavior flags.
+ * </ul>
+ *
+ * <p>This is NOT the class that manages the system VR mode lifecycle. The class responsible for
+ * handling everything related to VR mode state changes (e.g. the lifecycles of the associated
+ * VrListenerService, VrStateCallbacks, VR HAL etc.) is VrManagerService.
+ *
+ * <p>This class is exclusively for use by ActivityManagerService. Do not add callbacks or other
+ * functionality to this for things that belong in VrManagerService.
+ */
+final class VrController {
+ private static final String TAG = "VrController";
+
+ // VR state flags.
+ private static final int FLAG_NON_VR_MODE = 0;
+ private static final int FLAG_VR_MODE = 1;
+ private static final int FLAG_PERSISTENT_VR_MODE = 2;
+
+ // Invariants maintained for mVrState
+ //
+ // Always true:
+ // - Only a single VR-related thread will have elevated scheduling priorities at a time
+ // across all threads in all processes (and for all possible running modes).
+ //
+ // Always true while FLAG_PERSISTENT_VR_MODE is set:
+ // - An application has set a flag to run in persistent VR mode the next time VR mode is
+ // entered. The device may or may not be in VR mode.
+ // - mVrState will contain FLAG_PERSISTENT_VR_MODE
+ // - An application may set a persistent VR thread that gains elevated scheduling
+ // priorities via a call to setPersistentVrThread.
+ // - Calls to set a regular (non-persistent) VR thread via setVrThread will fail, and
+ // thread that had previously elevated its scheduling priority in this way is returned
+ // to its normal scheduling priority.
+ //
+ // Always true while FLAG_VR_MODE is set:
+ // - The current top application is running in VR mode.
+ // - mVrState will contain FLAG_VR_MODE
+ //
+ // While FLAG_VR_MODE is set without FLAG_PERSISTENT_VR_MODE:
+ // - The current top application may set one of its threads to run at an elevated
+ // scheduling priority via a call to setVrThread.
+ //
+ // While FLAG_VR_MODE is set with FLAG_PERSISTENT_VR_MODE:
+ // - The current top application may NOT set one of its threads to run at an elevated
+ // scheduling priority via a call to setVrThread (instead, the persistent VR thread will
+ // be kept if an application has set one).
+ //
+ // While mVrState == FLAG_NON_VR_MODE:
+ // - Calls to setVrThread will fail.
+ // - Calls to setPersistentVrThread will fail.
+ // - No threads will have elevated scheduling priority for VR.
+ //
+ private int mVrState = FLAG_NON_VR_MODE;
+
+ // The single VR render thread on the device that is given elevated scheduling priority.
+ private int mVrRenderThreadTid = 0;
+
+ private final Object mGlobalAmLock;
+
+ private final IPersistentVrStateCallbacks mPersistentVrModeListener =
+ new IPersistentVrStateCallbacks.Stub() {
+ @Override
+ public void onPersistentVrStateChanged(boolean enabled) {
+ synchronized(mGlobalAmLock) {
+ // Note: This is the only place where mVrState should have its
+ // FLAG_PERSISTENT_VR_MODE setting changed.
+ if (enabled) {
+ setVrRenderThreadLocked(0, ProcessList.SCHED_GROUP_TOP_APP, true);
+ mVrState |= FLAG_PERSISTENT_VR_MODE;
+ } else {
+ setPersistentVrRenderThreadLocked(0, true);
+ mVrState &= ~FLAG_PERSISTENT_VR_MODE;
+ }
+ }
+ }
+ };
+
+ /**
+ * Create new VrController instance.
+ *
+ * @param globalAmLock the global ActivityManagerService lock.
+ */
+ public VrController(final Object globalAmLock) {
+ mGlobalAmLock = globalAmLock;
+ }
+
+ /**
+ * Called when ActivityManagerService receives its systemReady call during boot.
+ */
+ public void onSystemReady() {
+ VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class);
+ if (vrManagerInternal != null) {
+ vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
+ }
+ }
+
+ /**
+ * Called when ActivityManagerService's TOP_APP process has changed.
+ *
+ * <p>Note: This must be called with the global ActivityManagerService lock held.
+ *
+ * @param proc is the ProcessRecord of the process that entered or left the TOP_APP scheduling
+ * group.
+ */
+ public void onTopProcChangedLocked(ProcessRecord proc) {
+ if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, true);
+ } else {
+ if (proc.vrThreadTid == mVrRenderThreadTid) {
+ clearVrRenderThreadLocked(true);
+ }
+ }
+ }
+
+ /**
+ * Called when ActivityManagerService is switching VR mode for the TOP_APP process.
+ *
+ * @param record the ActivityRecord of the activity changing the system VR mode.
+ * @return {@code true} if the VR state changed.
+ */
+ public boolean onVrModeChanged(ActivityRecord record) {
+ // This message means that the top focused activity enabled VR mode (or an activity
+ // that previously set this has become focused).
+ VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+ if (vrService == null) {
+ // VR mode isn't supported on this device.
+ return false;
+ }
+ boolean vrMode;
+ ComponentName requestedPackage;
+ ComponentName callingPackage;
+ int userId;
+ boolean changed = false;
+ synchronized (mGlobalAmLock) {
+ vrMode = record.requestedVrComponent != null;
+ requestedPackage = record.requestedVrComponent;
+ userId = record.userId;
+ callingPackage = record.info.getComponentName();
+
+ // Tell the VrController that a VR mode change is requested.
+ changed = changeVrModeLocked(vrMode, record.app);
+ }
+
+ // Tell VrManager that a VR mode changed is requested, VrManager will handle
+ // notifying all non-AM dependencies if needed.
+ vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
+ return changed;
+ }
+
+ /**
+ * Called to set an application's VR thread.
+ *
+ * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
+ * or the scheduling group of the thread is not for the current top app. If this succeeds, any
+ * previous VR thread will be returned to a normal sheduling priority; if this fails, the
+ * scheduling for the previous thread will be unaffected.
+ *
+ * <p>Note: This must be called with the global ActivityManagerService lock and the
+ * mPidsSelfLocked object locks held.
+ *
+ * @param tid the tid of the thread to set, or 0 to unset the current thread.
+ * @param pid the pid of the process owning the thread to set.
+ * @param proc the ProcessRecord of the process owning the thread to set.
+ */
+ public void setVrThreadLocked(int tid, int pid, ProcessRecord proc) {
+ if (hasPersistentVrFlagSet()) {
+ Slog.w(TAG, "VR thread cannot be set in persistent VR mode!");
+ return;
+ }
+ if (proc == null) {
+ Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
+ return;
+ }
+ if (tid != 0) {
+ enforceThreadInProcess(tid, pid);
+ }
+ if (!inVrMode()) {
+ Slog.w(TAG, "VR thread cannot be set when not in VR mode!");
+ } else {
+ setVrRenderThreadLocked(tid, proc.curSchedGroup, false);
+ }
+ proc.vrThreadTid = (tid > 0) ? tid : 0;
+ }
+
+ /**
+ * Called to set an application's persistent VR thread.
+ *
+ * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
+ * any previous VR thread will be returned to a normal sheduling priority; if this fails,
+ * the scheduling for the previous thread will be unaffected.
+ *
+ * <p>Note: This must be called with the global ActivityManagerService lock and the
+ * mPidsSelfLocked object locks held.
+ *
+ * @param tid the tid of the thread to set, or 0 to unset the current thread.
+ * @param pid the pid of the process owning the thread to set.
+ * @param proc the ProcessRecord of the process owning the thread to set.
+ */
+ public void setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc) {
+ if (!hasPersistentVrFlagSet()) {
+ Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!");
+ return;
+ }
+ if (proc == null) {
+ Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
+ return;
+ }
+ if (tid != 0) {
+ enforceThreadInProcess(tid, pid);
+ }
+ setPersistentVrRenderThreadLocked(tid, false);
+ }
+
+ /**
+ * Return {@code true} when UI features incompatible with VR mode should be disabled.
+ *
+ * <p>Note: This must be called with the global ActivityManagerService lock held.
+ */
+ public boolean shouldDisableNonVrUiLocked() {
+ return mVrState != FLAG_NON_VR_MODE;
+ }
+
+ /**
+ * Called when to update this VrController instance's state when the system VR mode is being
+ * changed.
+ *
+ * <p>Note: This must be called with the global ActivityManagerService lock held.
+ *
+ * @param vrMode {@code true} if the system VR mode is being enabled.
+ * @param proc the ProcessRecord of the process enabling the system VR mode.
+ *
+ * @return {@code true} if our state changed.
+ */
+ private boolean changeVrModeLocked(boolean vrMode, ProcessRecord proc) {
+ final int oldVrState = mVrState;
+
+ // This is the only place where mVrState should have its FLAG_VR_MODE setting
+ // changed.
+ if (vrMode) {
+ mVrState |= FLAG_VR_MODE;
+ } else {
+ mVrState &= ~FLAG_VR_MODE;
+ }
+
+ boolean changed = (oldVrState != mVrState);
+
+ if (changed) {
+ if (proc != null) {
+ if (proc.vrThreadTid > 0) {
+ setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, false);
+ }
+ } else {
+ clearVrRenderThreadLocked(false);
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * Set the given thread as the new VR thread, and give it special scheduling priority.
+ *
+ * <p>If the current thread is this thread, do nothing. If the current thread is different from
+ * the given thread, the current thread will be returned to a normal scheduling priority.
+ *
+ * @param newTid the tid of the thread to set, or 0 to unset the current thread.
+ * @param suppressLogs {@code true} if any error logging should be disabled.
+ *
+ * @return the tid of the thread configured to run at the scheduling priority for VR
+ * mode after this call completes (this may be the previous thread).
+ */
+ private int updateVrRenderThreadLocked(int newTid, boolean suppressLogs) {
+ if (mVrRenderThreadTid == newTid) {
+ return mVrRenderThreadTid;
+ }
+
+ if (mVrRenderThreadTid > 0) {
+ ActivityManagerService.scheduleAsRegularPriority(mVrRenderThreadTid, suppressLogs);
+ mVrRenderThreadTid = 0;
+ }
+
+ if (newTid > 0) {
+ mVrRenderThreadTid = newTid;
+ ActivityManagerService.scheduleAsFifoPriority(mVrRenderThreadTid, suppressLogs);
+ }
+ return mVrRenderThreadTid;
+ }
+
+ /**
+ * Set special scheduling for the given application persistent VR thread, if allowed.
+ *
+ * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
+ * any previous VR thread will be returned to a normal sheduling priority; if this fails,
+ * the scheduling for the previous thread will be unaffected.
+ *
+ * @param newTid the tid of the thread to set, or 0 to unset the current thread.
+ * @param suppressLogs {@code true} if any error logging should be disabled.
+ *
+ * @return the tid of the thread configured to run at the scheduling priority for VR
+ * mode after this call completes (this may be the previous thread).
+ */
+ private int setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs) {
+ if (!hasPersistentVrFlagSet()) {
+ if (!suppressLogs) {
+ Slog.w(TAG, "Failed to set persistent VR thread, "
+ + "system not in persistent VR mode.");
+ }
+ return mVrRenderThreadTid;
+ }
+ return updateVrRenderThreadLocked(newTid, suppressLogs);
+ }
+
+ /**
+ * Set special scheduling for the given application VR thread, if allowed.
+ *
+ * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
+ * or the scheduling group of the thread is not for the current top app. If this succeeds, any
+ * previous VR thread will be returned to a normal sheduling priority; if this fails, the
+ * scheduling for the previous thread will be unaffected.
+ *
+ * @param newTid the tid of the thread to set, or 0 to unset the current thread.
+ * @param schedGroup the current scheduling group of the thread to set.
+ * @param suppressLogs {@code true} if any error logging should be disabled.
+ *
+ * @return the tid of the thread configured to run at the scheduling priority for VR
+ * mode after this call completes (this may be the previous thread).
+ */
+ private int setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs) {
+ boolean inVr = inVrMode();
+ boolean inPersistentVr = hasPersistentVrFlagSet();
+ if (!inVr || inPersistentVr || schedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+ if (!suppressLogs) {
+ String reason = "caller is not the current top application.";
+ if (!inVr) {
+ reason = "system not in VR mode.";
+ } else if (inPersistentVr) {
+ reason = "system in persistent VR mode.";
+ }
+ Slog.w(TAG, "Failed to set VR thread, " + reason);
+ }
+ return mVrRenderThreadTid;
+ }
+ return updateVrRenderThreadLocked(newTid, suppressLogs);
+ }
+
+ /**
+ * Unset any special scheduling used for the current VR render thread, and return it to normal
+ * scheduling priority.
+ *
+ * @param suppressLogs {@code true} if any error logging should be disabled.
+ */
+ private void clearVrRenderThreadLocked(boolean suppressLogs) {
+ updateVrRenderThreadLocked(0, suppressLogs);
+ }
+
+ /**
+ * Check that the given tid is running in the process for the given pid, and throw an exception
+ * if not.
+ */
+ private void enforceThreadInProcess(int tid, int pid) {
+ if (!Process.isThreadInProcess(pid, tid)) {
+ throw new IllegalArgumentException("VR thread does not belong to process");
+ }
+ }
+
+ /**
+ * True when the system is in VR mode.
+ */
+ private boolean inVrMode() {
+ return (mVrState & FLAG_VR_MODE) != 0;
+ }
+
+ /**
+ * True when the persistent VR mode flag has been set.
+ *
+ * Note: Currently this does not necessarily mean that the system is in VR mode.
+ */
+ private boolean hasPersistentVrFlagSet() {
+ return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 44c715b..64ee1e9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1294,19 +1294,14 @@
sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
}
- private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
- boolean fromAssistant) {
+ private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel) {
if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
// cancel
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
null);
}
- if (fromAssistant) {
- mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel);
- } else {
- mRankingHelper.updateNotificationChannel(pkg, uid, channel);
- }
+ mRankingHelper.updateNotificationChannel(pkg, uid, channel);
synchronized (mNotificationLock) {
final int N = mNotificationList.size();
@@ -1709,7 +1704,7 @@
NotificationChannel channel) {
enforceSystemOrSystemUI("Caller not system or systemui");
Preconditions.checkNotNull(channel);
- updateNotificationChannelInt(pkg, uid, channel, false);
+ updateNotificationChannelInt(pkg, uid, channel);
}
@Override
@@ -2646,47 +2641,6 @@
Binder.restoreCallingIdentity(identity);
}
}
-
- @Override
- public void createNotificationChannelFromAssistant(INotificationListener token, String pkg,
- NotificationChannel channel) throws RemoteException {
- ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
- int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
- mRankingHelper.createNotificationChannel(pkg, uid, channel, false /* fromTargetApp */);
- savePolicyFile();
- }
-
- @Override
- public void deleteNotificationChannelFromAssistant(INotificationListener token, String pkg,
- String channelId) throws RemoteException {
- ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
- throw new IllegalArgumentException("Cannot delete default channel");
- }
-
- int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
- cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
- info.userid, REASON_CHANNEL_BANNED, null);
- mRankingHelper.deleteNotificationChannel(pkg, uid, channelId);
- savePolicyFile();
- }
-
- @Override
- public void updateNotificationChannelFromAssistant(INotificationListener token, String pkg,
- NotificationChannel channel) throws RemoteException {
- ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
- Preconditions.checkNotNull(channel);
- int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
- updateNotificationChannelInt(pkg, uid, channel, true);
- }
-
- @Override
- public ParceledListSlice<NotificationChannel> getNotificationChannelsFromAssistant(
- INotificationListener token, String pkg) throws RemoteException {
- ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
- int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
- return mRankingHelper.getNotificationChannels(pkg, uid, false /* includeDeleted */);
- }
};
private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
@@ -2695,17 +2649,10 @@
}
if (adjustment.getSignals() != null) {
Bundle.setDefusable(adjustment.getSignals(), true);
- final String overrideChannelId =
- adjustment.getSignals().getString(Adjustment.KEY_CHANNEL_ID, null);
final ArrayList<String> people =
adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
final ArrayList<SnoozeCriterion> snoozeCriterionList =
adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
- if (!TextUtils.isEmpty(overrideChannelId)) {
- n.updateNotificationChannel(mRankingHelper.getNotificationChannel(
- n.sbn.getPackageName(), n.sbn.getUid(), overrideChannelId,
- false /* includeDeleted */));
- }
n.setPeopleOverride(people);
n.setSnoozeCriteria(snoozeCriterionList);
}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index e13df19..4d19b52 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -37,7 +37,6 @@
void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp);
void updateNotificationChannel(String pkg, int uid, NotificationChannel channel);
- void updateNotificationChannelFromAssistant(String pkg, int uid, NotificationChannel channel);
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 65aaee0..b63b05f 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -609,55 +609,6 @@
}
@Override
- public void updateNotificationChannelFromAssistant(String pkg, int uid,
- NotificationChannel updatedChannel) {
- Record r = getOrCreateRecord(pkg, uid);
- if (r == null) {
- throw new IllegalArgumentException("Invalid package");
- }
- NotificationChannel channel = r.channels.get(updatedChannel.getId());
- if (channel == null || channel.isDeleted()) {
- throw new IllegalArgumentException("Channel does not exist");
- }
-
- if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0) {
- channel.setImportance(updatedChannel.getImportance());
- }
- if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
- channel.enableLights(updatedChannel.shouldShowLights());
- channel.setLightColor(updatedChannel.getLightColor());
- }
- if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_PRIORITY) == 0) {
- channel.setBypassDnd(updatedChannel.canBypassDnd());
- }
- if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_SOUND) == 0) {
- channel.setSound(updatedChannel.getSound(), updatedChannel.getAudioAttributes());
- }
- if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
- channel.enableVibration(updatedChannel.shouldVibrate());
- channel.setVibrationPattern(updatedChannel.getVibrationPattern());
- }
- if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_VISIBILITY) == 0) {
- if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
- channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
- } else {
- channel.setLockscreenVisibility(updatedChannel.getLockscreenVisibility());
- }
- }
- if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_SHOW_BADGE) == 0) {
- channel.setShowBadge(updatedChannel.canShowBadge());
- }
- if (updatedChannel.isDeleted()) {
- channel.setDeleted(true);
- }
- // Assistant cannot change the group
-
- MetricsLogger.action(getChannelLog(channel, pkg));
- r.channels.put(channel.getId(), channel);
- updateConfig();
- }
-
- @Override
public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
boolean includeDeleted) {
Preconditions.checkNotNull(pkg);
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 07c9dec..2100038 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -98,4 +98,8 @@
public boolean isInstant() {
return (protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0;
}
+
+ public boolean isRuntimeOnly() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index f6e96b2..c4cc4fb 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -64,9 +64,10 @@
private volatile boolean mBindRequested;
private IInstantAppResolver mRemoteInstance;
- public EphemeralResolverConnection(Context context, ComponentName componentName) {
+ public EphemeralResolverConnection(
+ Context context, ComponentName componentName, String action) {
mContext = context;
- mIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE).setComponent(componentName);
+ mIntent = new Intent(action).setComponent(componentName);
}
public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[],
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 0ae5f31..89a303d 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -272,6 +272,7 @@
} else {
// Deleting an app prunes all instant state such as cookie
deleteDir(getInstantApplicationDir(pkg.packageName, userId));
+ mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
removeAppLPw(userId, ps.appId);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index acbd446..781be34 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.PowerManager;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -259,9 +260,11 @@
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
int dexoptFlags = getDexFlags(info, compilerFilter) | DEXOPT_SECONDARY_DEX;
// Check the app storage and add the appropriate flags.
- if (info.dataDir.equals(info.deviceProtectedDataDir)) {
+ if (info.deviceProtectedDataDir != null &&
+ FileUtils.contains(info.deviceProtectedDataDir, path)) {
dexoptFlags |= DEXOPT_STORAGE_DE;
- } else if (info.dataDir.equals(info.credentialProtectedDataDir)) {
+ } else if (info.credentialProtectedDataDir != null &&
+ FileUtils.contains(info.credentialProtectedDataDir, path)) {
dexoptFlags |= DEXOPT_STORAGE_CE;
} else {
Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f62f115..ce4d7ee 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -849,8 +849,7 @@
/** Component used to show resolver settings for Instant Apps */
final ComponentName mInstantAppResolverSettingsComponent;
- /** Component used to install ephemeral applications */
- ComponentName mInstantAppInstallerComponent;
+ /** Activity used to install instant applications */
ActivityInfo mInstantAppInstallerActivity;
final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
@@ -2056,6 +2055,7 @@
}
if (bp != null && (bp.isRuntime() || bp.isDevelopment())
&& (!instantApp || bp.isInstant())
+ && (supportsRuntimePermissions || !bp.isRuntimeOnly())
&& (grantedPermissions == null
|| ArrayUtils.contains(grantedPermissions, permission))) {
final int flags = permissionsState.getPermissionFlags(permission, userId);
@@ -2813,20 +2813,22 @@
}
mInstallerService = new PackageInstallerService(context, this);
- final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr();
- if (ephemeralResolverComponent != null) {
+ final Pair<ComponentName, String> instantAppResolverComponent =
+ getInstantAppResolverLPr();
+ if (instantAppResolverComponent != null) {
if (DEBUG_EPHEMERAL) {
- Slog.d(TAG, "Set ephemeral resolver: " + ephemeralResolverComponent);
+ Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent);
}
- mInstantAppResolverConnection =
- new EphemeralResolverConnection(mContext, ephemeralResolverComponent);
+ mInstantAppResolverConnection = new EphemeralResolverConnection(
+ mContext, instantAppResolverComponent.first,
+ instantAppResolverComponent.second);
mInstantAppResolverSettingsComponent =
- getEphemeralResolverSettingsLPr(ephemeralResolverComponent);
+ getInstantAppResolverSettingsLPr(instantAppResolverComponent.first);
} else {
mInstantAppResolverConnection = null;
mInstantAppResolverSettingsComponent = null;
}
- updateInstantAppInstallerLocked();
+ updateInstantAppInstallerLocked(null);
// Read and update the usage of dex files.
// Do this at the end of PM init so that all the packages have their
@@ -2866,22 +2868,15 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- private void updateInstantAppInstallerLocked() {
- final ComponentName oldInstantAppInstallerComponent = mInstantAppInstallerComponent;
- final ActivityInfo newInstantAppInstaller = getEphemeralInstallerLPr();
- ComponentName newInstantAppInstallerComponent = newInstantAppInstaller == null
- ? null : newInstantAppInstaller.getComponentName();
-
- if (newInstantAppInstallerComponent != null
- && !newInstantAppInstallerComponent.equals(oldInstantAppInstallerComponent)) {
- if (DEBUG_EPHEMERAL) {
- Slog.d(TAG, "Set ephemeral installer: " + newInstantAppInstallerComponent);
- }
- setUpInstantAppInstallerActivityLP(newInstantAppInstaller);
- } else if (DEBUG_EPHEMERAL && newInstantAppInstallerComponent == null) {
- Slog.d(TAG, "Unset ephemeral installer; none available");
+ private void updateInstantAppInstallerLocked(String modifiedPackage) {
+ // we're only interested in updating the installer appliction when 1) it's not
+ // already set or 2) the modified package is the installer
+ if (mInstantAppInstallerActivity != null
+ && !mInstantAppInstallerActivity.getComponentName().getPackageName()
+ .equals(modifiedPackage)) {
+ return;
}
- mInstantAppInstallerComponent = newInstantAppInstallerComponent;
+ setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
}
private static File preparePackageParserCache(boolean isUpgrade) {
@@ -3045,7 +3040,7 @@
}
}
- private @Nullable ComponentName getEphemeralResolverLPr() {
+ private @Nullable Pair<ComponentName, String> getInstantAppResolverLPr() {
final String[] packageArray =
mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) {
@@ -3060,7 +3055,8 @@
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE
| (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
- final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
+ String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE;
+ final Intent resolverIntent = new Intent(actionName);
List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
// temporarily look for the old action
@@ -3068,7 +3064,8 @@
if (DEBUG_EPHEMERAL) {
Slog.d(TAG, "Ephemeral resolver not found with new action; try old one");
}
- resolverIntent.setAction(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE);
+ actionName = Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE;
+ resolverIntent.setAction(actionName);
resolvers = queryIntentServicesInternal(resolverIntent, null,
resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
}
@@ -3101,7 +3098,7 @@
Slog.v(TAG, "Ephemeral resolver found;"
+ " pkg: " + packageName + ", info:" + info);
}
- return new ComponentName(packageName, info.serviceInfo.name);
+ return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName);
}
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Ephemeral resolver NOT found");
@@ -3109,7 +3106,7 @@
return null;
}
- private @Nullable ActivityInfo getEphemeralInstallerLPr() {
+ private @Nullable ActivityInfo getInstantAppInstallerLPr() {
final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
@@ -3151,7 +3148,7 @@
}
}
- private @Nullable ComponentName getEphemeralResolverSettingsLPr(
+ private @Nullable ComponentName getInstantAppResolverSettingsLPr(
@NonNull ComponentName resolver) {
final Intent intent = new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS)
.addCategory(Intent.CATEGORY_DEFAULT)
@@ -5732,7 +5729,7 @@
if (mInstantAppResolverConnection == null) {
return false;
}
- if (mInstantAppInstallerComponent == null) {
+ if (mInstantAppInstallerActivity == null) {
return false;
}
if (intent.getComponent() != null) {
@@ -11411,6 +11408,8 @@
for (int i=0; i<N; i++) {
final String name = pkg.requestedPermissions.get(i);
final BasePermission bp = mSettings.mPermissions.get(name);
+ final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+ >= Build.VERSION_CODES.M;
if (DEBUG_INSTALL) {
Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
@@ -11432,6 +11431,12 @@
continue;
}
+ if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
+ Log.i(TAG, "Denying runtime-only permission " + bp.name + " for package "
+ + pkg.packageName);
+ continue;
+ }
+
final String perm = bp.name;
boolean allowedSig = false;
int grant = GRANT_DENIED;
@@ -11447,8 +11452,6 @@
}
final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
- >= Build.VERSION_CODES.M;
switch (level) {
case PermissionInfo.PROTECTION_NORMAL: {
// For all apps normal permissions are install time ones.
@@ -17081,7 +17084,7 @@
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
updateSequenceNumberLP(pkgName, res.newUsers);
- updateInstantAppInstallerLocked();
+ updateInstantAppInstallerLocked(pkgName);
}
}
}
@@ -17657,7 +17660,7 @@
mInstantAppRegistry.onPackageUninstalledLPw(pkg, info.removedUsers);
}
updateSequenceNumberLP(packageName, info.removedUsers);
- updateInstantAppInstallerLocked();
+ updateInstantAppInstallerLocked(packageName);
}
}
}
@@ -20020,7 +20023,7 @@
updateSequenceNumberLP(packageName, new int[] { userId });
final long callingId = Binder.clearCallingIdentity();
try {
- updateInstantAppInstallerLocked();
+ updateInstantAppInstallerLocked(packageName);
} finally {
Binder.restoreCallingIdentity(callingId);
}
@@ -23212,7 +23215,8 @@
@Override
public boolean isInstantAppInstallerComponent(ComponentName component) {
synchronized (mPackages) {
- return component != null && component.equals(mInstantAppInstallerComponent);
+ return mInstantAppInstallerActivity != null
+ && mInstantAppInstallerActivity.getComponentName().equals(component);
}
}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index c693a47..3d7cedc 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -19,7 +19,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
+import android.os.FileUtils;
import android.os.RemoteException;
import android.os.storage.StorageManager;
import android.os.UserHandle;
@@ -93,7 +93,7 @@
* Note that this method is invoked when apps load dex files and it should
* return as fast as possible.
*
- * @param loadingPackage the package performing the load
+ * @param loadingAppInfo the package performing the load
* @param dexPaths the list of dex files being loaded
* @param loaderIsa the ISA of the app loading the dex files
* @param loaderUserId the user id which runs the code loading the dex files
@@ -191,8 +191,7 @@
throw new IllegalArgumentException(
"notifyPackageInstalled called with USER_ALL");
}
- cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
- pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
+ cachePackageInfo(pi, userId);
}
/**
@@ -231,13 +230,32 @@
}
}
- public void cachePackageCodeLocation(String packageName, String baseCodePath,
- String[] splitCodePaths, String dataDir, int userId) {
+ /**
+ * Caches the code location from the given package info.
+ */
+ private void cachePackageInfo(PackageInfo pi, int userId) {
+ ApplicationInfo ai = pi.applicationInfo;
+ String[] dataDirs = new String[] {ai.dataDir, ai.deviceProtectedDataDir,
+ ai.credentialProtectedDataDir};
+ cachePackageCodeLocation(pi.packageName, ai.sourceDir, ai.splitSourceDirs,
+ dataDirs, userId);
+ }
+
+ private void cachePackageCodeLocation(String packageName, String baseCodePath,
+ String[] splitCodePaths, String[] dataDirs, int userId) {
PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName,
new PackageCodeLocations(packageName, baseCodePath, splitCodePaths));
pcl.updateCodeLocation(baseCodePath, splitCodePaths);
- if (dataDir != null) {
- pcl.mergeAppDataDirs(dataDir, userId);
+ if (dataDirs != null) {
+ for (String dataDir : dataDirs) {
+ // The set of data dirs includes deviceProtectedDataDir and
+ // credentialProtectedDataDir which might be null for shared
+ // libraries. Currently we don't track these but be lenient
+ // and check in case we ever decide to store their usage data.
+ if (dataDir != null) {
+ pcl.mergeAppDataDirs(dataDir, userId);
+ }
+ }
}
}
@@ -250,8 +268,7 @@
int userId = entry.getKey();
for (PackageInfo pi : packageInfoList) {
// Cache the code locations.
- cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
- pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
+ cachePackageInfo(pi, userId);
// Cache a map from package name to the set of user ids who installed the package.
// We will use it to sync the data and remove obsolete entries from
@@ -329,6 +346,7 @@
mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId());
continue;
}
+
int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps());
success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED);
@@ -350,7 +368,7 @@
// Nothing to reconcile.
return;
}
- Set<String> dexFilesToRemove = new HashSet<>();
+
boolean updated = false;
for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) {
String dexPath = entry.getKey();
@@ -378,14 +396,16 @@
}
ApplicationInfo info = pkg.applicationInfo;
int flags = 0;
- if (info.dataDir.equals(info.deviceProtectedDataDir)) {
+ if (info.deviceProtectedDataDir != null &&
+ FileUtils.contains(info.deviceProtectedDataDir, dexPath)) {
flags |= StorageManager.FLAG_STORAGE_DE;
- } else if (info.dataDir.equals(info.credentialProtectedDataDir)) {
+ } else if (info.credentialProtectedDataDir!= null &&
+ FileUtils.contains(info.credentialProtectedDataDir, dexPath)) {
flags |= StorageManager.FLAG_STORAGE_CE;
} else {
- Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
- updated = mPackageDexUsage.removeUserPackage(
- packageName, dexUseInfo.getOwnerUserId()) || updated;
+ Slog.e(TAG, "Could not infer CE/DE storage for path " + dexPath);
+ updated = mPackageDexUsage.removeDexFile(
+ packageName, dexPath, dexUseInfo.getOwnerUserId()) || updated;
continue;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 95fb5af..6633efd 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5255,8 +5255,11 @@
}
}
+ // Don't allow snapshots to influence SystemUI visibility flags.
+ // TODO: Revisit this once SystemUI flags for snapshots are handled correctly
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
- && attrs.type < FIRST_SYSTEM_WINDOW;
+ && attrs.type < FIRST_SYSTEM_WINDOW
+ && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0;
final int stackId = win.getStackId();
if (mTopFullscreenOpaqueWindowState == null && visible) {
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
diff --git a/services/core/java/com/android/server/vr/CompatibilityDisplay.java b/services/core/java/com/android/server/vr/CompatibilityDisplay.java
index 772fc26..ae1d50f 100644
--- a/services/core/java/com/android/server/vr/CompatibilityDisplay.java
+++ b/services/core/java/com/android/server/vr/CompatibilityDisplay.java
@@ -95,15 +95,20 @@
* Creates and Destroys the virtual display depending on the current state of VrMode.
*/
private void updateVirtualDisplay() {
+ boolean createVirtualDisplay = "true".equals(SystemProperties.get("vr_virtualdisplay"));
if (DEBUG) {
- Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", override: " + mIsVrModeOverrideEnabled);
+ Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", createVD: " + createVirtualDisplay +
+ ", override: " + mIsVrModeOverrideEnabled);
}
- if (mIsVrModeEnabled || mIsVrModeOverrideEnabled) {
+ if (mIsVrModeEnabled || (createVirtualDisplay && mIsVrModeOverrideEnabled)) {
// TODO: Consider not creating the display until ActivityManager needs one on
// which to display a 2D application.
- startVirtualDisplay();
- startImageReader();
+ // TODO: STOPSHIP Remove createVirtualDisplay conditional before launching.
+ if (createVirtualDisplay) {
+ startVirtualDisplay();
+ startImageReader();
+ }
} else {
// Stop virtual display to test exit condition
stopVirtualDisplay();
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 2bc3c5f..4b4be40 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -566,7 +566,7 @@
return false;
}
- mContainer.startingData = new SnapshotStartingData(mService, snapshot);
+ mContainer.startingData = new SnapshotStartingData(mService, snapshot.getSnapshot());
scheduleAddStartingWindow();
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4ebf1fc..21be742 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -665,7 +665,8 @@
}
}
}
- if (!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening()) {
+ if ((!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening()) ||
+ winAnimator.isDummyAnimation()) {
// Updates the shown frame before we set up the surface. This is needed
// because the resizing could change the top-left position (in addition to
// size) of the window. setSurfaceBoundariesLocked uses mShownPosition to
@@ -2920,11 +2921,7 @@
if (stack != null) {
stack.getBounds(frame);
}
-
- // We want to screenshot with the exact bounds of the surface of the app. Thus,
- // intersect it with the frame.
- frame.intersect(w.mFrame);
- }else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) {
+ } else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) {
final Rect wf = w.mFrame;
final Rect cr = w.mContentInsets;
int left = wf.left + cr.left;
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
index 35f35db..e73d4d25 100644
--- a/services/core/java/com/android/server/wm/SnapshotStartingData.java
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import android.app.ActivityManager.TaskSnapshot;
import android.graphics.GraphicBuffer;
import android.view.WindowManagerPolicy.StartingSurface;
@@ -26,9 +25,9 @@
class SnapshotStartingData extends StartingData {
private final WindowManagerService mService;
- private final TaskSnapshot mSnapshot;
+ private final GraphicBuffer mSnapshot;
- SnapshotStartingData(WindowManagerService service, TaskSnapshot snapshot) {
+ SnapshotStartingData(WindowManagerService service, GraphicBuffer snapshot) {
super(service);
mService = service;
mSnapshot = snapshot;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b816d81..3ffb093 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,14 +17,15 @@
package com.android.server.wm;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -39,6 +40,7 @@
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.EventLogTags;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 48b01f4..b8d0b8c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -28,7 +28,6 @@
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Canvas;
import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
import android.os.Environment;
import android.util.ArraySet;
import android.view.WindowManagerPolicy.StartingSurface;
@@ -153,7 +152,7 @@
* MANAGER LOCK WHEN CALLING THIS METHOD!
*/
StartingSurface createStartingSurface(AppWindowToken token,
- TaskSnapshot snapshot) {
+ GraphicBuffer snapshot) {
return TaskSnapshotSurface.create(mService, token, snapshot);
}
@@ -167,17 +166,8 @@
if (buffer == null) {
return null;
}
- final WindowState mainWindow = top.findMainWindow();
return new TaskSnapshot(buffer, top.getConfiguration().orientation,
- minRect(mainWindow.mContentInsets, mainWindow.mStableInsets), false /* reduced */,
- 1f /* scale */);
- }
-
- private Rect minRect(Rect rect1, Rect rect2) {
- return new Rect(Math.min(rect1.left, rect2.left),
- Math.min(rect1.top, rect2.top),
- Math.min(rect1.right, rect2.right),
- Math.min(rect1.bottom, rect2.bottom));
+ top.findMainWindow().mStableInsets, false /* reduced */, 1f /* scale */);
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 1591e48..04403e2 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -16,35 +16,20 @@
package com.android.server.wm;
-import static android.graphics.Color.WHITE;
-import static android.graphics.Color.alpha;
-import static android.view.SurfaceControl.HIDDEN;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
-import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
-import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
-import static com.android.internal.policy.DecorView.getColorViewLeftInset;
-import static com.android.internal.policy.DecorView.getColorViewTopInset;
-import static com.android.internal.policy.DecorView.getNavigationBarRect;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManager.TaskDescription;
-import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -52,22 +37,17 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.view.IWindowSession;
import android.view.Surface;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicy.StartingSurface;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.DecorView;
import com.android.internal.view.BaseIWindow;
/**
@@ -77,57 +57,19 @@
*/
class TaskSnapshotSurface implements StartingSurface {
- private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450;
-
- /**
- * When creating the starting window, we use the exact same layout flags such that we end up
- * with a window with the exact same dimensions etc. However, these flags are not used in layout
- * and might cause other side effects so we exclude them.
- */
- private static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
- | FLAG_NOT_TOUCHABLE
- | FLAG_NOT_TOUCH_MODAL
- | FLAG_ALT_FOCUSABLE_IM
- | FLAG_NOT_FOCUSABLE
- | FLAG_HARDWARE_ACCELERATED
- | FLAG_IGNORE_CHEEK_PRESSES
- | FLAG_LOCAL_FOCUS_MODE
- | FLAG_SLIPPERY
- | FLAG_WATCH_OUTSIDE_TOUCH
- | FLAG_SPLIT_TOUCH
- | FLAG_SCALED
- | FLAG_SECURE;
-
private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM;
private static final int MSG_REPORT_DRAW = 0;
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
private final Window mWindow;
private final Surface mSurface;
- private SurfaceControl mChildSurfaceControl;
private final IWindowSession mSession;
private final WindowManagerService mService;
- private final Rect mTaskBounds;
- private final Rect mStableInsets = new Rect();
- private final Rect mContentInsets = new Rect();
- private final Rect mFrame = new Rect();
- private final TaskSnapshot mSnapshot;
- private final CharSequence mTitle;
private boolean mHasDrawn;
private boolean mReportNextDraw;
- private long mShownTime;
- private final Handler mHandler;
- private final boolean mSizeMismatch;
- private final Paint mBackgroundPaint = new Paint();
- private final Paint mStatusBarPaint = new Paint();
- private final Paint mNavigationBarPaint = new Paint();
- private final int mStatusBarColor;
- private final int mNavigationBarColor;
- private final int mSysUiVis;
- private final int mWindowFlags;
- private final int mWindowPrivateFlags;
+ private Paint mFillBackgroundPaint = new Paint();
static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
- TaskSnapshot snapshot) {
+ GraphicBuffer snapshot) {
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
final Window window = new Window();
@@ -136,51 +78,32 @@
final Surface surface = new Surface();
final Rect tmpRect = new Rect();
final Rect tmpFrame = new Rect();
- final Rect taskBounds;
- final Rect tmpContentInsets = new Rect();
- final Rect tmpStableInsets = new Rect();
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
- int backgroundColor = WHITE;
- int statusBarColor = 0;
- int navigationBarColor = 0;
- final int sysUiVis;
- final int windowFlags;
- final int windowPrivateFlags;
+ int fillBackgroundColor = Color.WHITE;
synchronized (service.mWindowMap) {
- final WindowState mainWindow = token.findMainWindow();
- if (mainWindow == null) {
- Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token="
- + token);
- return null;
- }
- sysUiVis = mainWindow.getSystemUiVisibility();
- windowFlags = mainWindow.getAttrs().flags;
- windowPrivateFlags = mainWindow.getAttrs().privateFlags;
-
layoutParams.type = TYPE_APPLICATION_STARTING;
- layoutParams.format = snapshot.getSnapshot().getFormat();
- layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
+ layoutParams.format = snapshot.getFormat();
+ layoutParams.flags = FLAG_LAYOUT_INSET_DECOR
+ | FLAG_LAYOUT_IN_SCREEN
| FLAG_NOT_FOCUSABLE
- | FLAG_NOT_TOUCHABLE;
+ | FLAG_NOT_TOUCHABLE
+ | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT;
layoutParams.token = token.token;
layoutParams.width = LayoutParams.MATCH_PARENT;
layoutParams.height = LayoutParams.MATCH_PARENT;
- layoutParams.systemUiVisibility = sysUiVis;
+
+ // TODO: Inherit behavior whether to draw behind status bar/nav bar.
+ layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
final Task task = token.getTask();
if (task != null) {
- layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
+ layoutParams.setTitle(String.format(TITLE_FORMAT,task.mTaskId));
final TaskDescription taskDescription = task.getTaskDescription();
if (taskDescription != null) {
- backgroundColor = taskDescription.getBackgroundColor();
- statusBarColor = taskDescription.getStatusBarColor();
- navigationBarColor = taskDescription.getNavigationBarColor();
+ fillBackgroundColor = taskDescription.getBackgroundColor();
}
- taskBounds = new Rect();
- task.getBounds(taskBounds);
- } else {
- taskBounds = null;
}
}
try {
@@ -195,57 +118,31 @@
// Local call.
}
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
- surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
- navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds);
+ surface, fillBackgroundColor);
window.setOuter(snapshotSurface);
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
- tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
- tmpMergedConfiguration, surface);
+ tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpMergedConfiguration,
+ surface);
} catch (RemoteException e) {
// Local call.
}
- snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets);
- snapshotSurface.drawSnapshot();
+ snapshotSurface.drawSnapshot(snapshot);
return snapshotSurface;
}
@VisibleForTesting
TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
- TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
- int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
- Rect taskBounds) {
+ int fillBackgroundColor) {
mService = service;
- mHandler = new Handler(mService.mH.getLooper());
mSession = WindowManagerGlobal.getWindowSession();
mWindow = window;
mSurface = surface;
- mSnapshot = snapshot;
- mTitle = title;
- mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
- mTaskBounds = taskBounds;
- mSysUiVis = sysUiVis;
- mWindowFlags = windowFlags;
- mWindowPrivateFlags = windowPrivateFlags;
- mSizeMismatch = (mFrame.width() != snapshot.getSnapshot().getWidth()
- || mFrame.height() != snapshot.getSnapshot().getHeight());
- mStatusBarColor = DecorView.calculateStatusBarColor(windowFlags,
- service.mContext.getColor(R.color.system_bar_background_semi_transparent),
- statusBarColor);
- mNavigationBarColor = navigationBarColor;
- mStatusBarPaint.setColor(mStatusBarColor);
- mNavigationBarPaint.setColor(navigationBarColor);
+ mFillBackgroundPaint.setColor(fillBackgroundColor);
}
@Override
public void remove() {
- synchronized (mService.mWindowMap) {
- final long now = SystemClock.uptimeMillis();
- if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
- mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
- return;
- }
- }
try {
mSession.remove(mWindow);
} catch (RemoteException e) {
@@ -253,149 +150,31 @@
}
}
- @VisibleForTesting
- void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) {
- mFrame.set(frame);
- mContentInsets.set(contentInsets);
- mStableInsets.set(stableInsets);
- }
-
- private void drawSnapshot() {
- final GraphicBuffer buffer = mSnapshot.getSnapshot();
- if (mSizeMismatch) {
- // The dimensions of the buffer and the window don't match, so attaching the buffer
- // will fail. Better create a child window with the exact dimensions and fill the parent
- // window with the background color!
- drawSizeMismatchSnapshot(buffer);
- } else {
- drawSizeMatchSnapshot(buffer);
- }
+ private void drawSnapshot(GraphicBuffer snapshot) {
+ mSurface.attachAndQueueBuffer(snapshot);
final boolean reportNextDraw;
synchronized (mService.mWindowMap) {
- mShownTime = SystemClock.uptimeMillis();
mHasDrawn = true;
reportNextDraw = mReportNextDraw;
}
if (reportNextDraw) {
reportDrawn();
}
- }
-
- private void drawSizeMatchSnapshot(GraphicBuffer buffer) {
- mSurface.attachAndQueueBuffer(buffer);
- mSurface.release();
- }
-
- private void drawSizeMismatchSnapshot(GraphicBuffer buffer) {
- final SurfaceSession session = new SurfaceSession(mSurface);
-
- // Keep a reference to it such that it doesn't get destroyed when finalized.
- mChildSurfaceControl = new SurfaceControl(session,
- mTitle + " - task-snapshot-surface",
- buffer.getWidth(), buffer.getHeight(), buffer.getFormat(), HIDDEN);
- Surface surface = new Surface();
- surface.copyFrom(mChildSurfaceControl);
-
- // Clip off ugly navigation bar.
- final Rect crop = calculateSnapshotCrop();
- final Rect frame = calculateSnapshotFrame(crop);
- SurfaceControl.openTransaction();
- try {
- // We can just show the surface here as it will still be hidden as the parent is
- // still hidden.
- mChildSurfaceControl.show();
- mChildSurfaceControl.setWindowCrop(crop);
- mChildSurfaceControl.setPosition(frame.left, frame.top);
- } finally {
- SurfaceControl.closeTransaction();
- }
- surface.attachAndQueueBuffer(buffer);
- surface.release();
-
- final Canvas c = mSurface.lockCanvas(null);
- drawBackgroundAndBars(c, frame);
- mSurface.unlockCanvasAndPost(c);
mSurface.release();
}
@VisibleForTesting
- Rect calculateSnapshotCrop() {
- final Rect rect = new Rect();
- rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
- final Rect insets = mSnapshot.getContentInsets();
-
- // Let's remove all system decorations except the status bar, but only if the task is at the
- // very top of the screen.
- rect.inset(insets.left, mTaskBounds.top != 0 ? insets.top : 0, insets.right, insets.bottom);
- return rect;
- }
-
- @VisibleForTesting
- Rect calculateSnapshotFrame(Rect crop) {
- final Rect frame = new Rect(crop);
-
- // By default, offset it to to top/left corner
- frame.offsetTo(-crop.left, -crop.top);
-
- // However, we also need to make space for the navigation bar on the left side.
- final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
- mContentInsets.left);
- frame.offset(colorViewLeftInset, 0);
- return frame;
- }
-
- @VisibleForTesting
- void drawBackgroundAndBars(Canvas c, Rect frame) {
- final int statusBarHeight = getStatusBarColorViewHeight();
- final boolean fillHorizontally = c.getWidth() > frame.right;
- final boolean fillVertically = c.getHeight() > frame.bottom;
+ void fillEmptyBackground(Canvas c, Bitmap b) {
+ final boolean fillHorizontally = c.getWidth() > b.getWidth();
+ final boolean fillVertically = c.getHeight() > b.getHeight();
if (fillHorizontally) {
- c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
- c.getWidth(), fillVertically
- ? frame.bottom
- : c.getHeight(),
- mBackgroundPaint);
+ c.drawRect(b.getWidth(), 0, c.getWidth(), fillVertically
+ ? b.getHeight()
+ : c.getHeight(),
+ mFillBackgroundPaint);
}
if (fillVertically) {
- c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
- }
- drawStatusBarBackground(c, frame, statusBarHeight);
- drawNavigationBarBackground(c);
- }
-
- private int getStatusBarColorViewHeight() {
- final boolean forceStatusBarBackground =
- (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
- if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mSysUiVis, mStatusBarColor, mWindowFlags, forceStatusBarBackground)) {
- return getColorViewTopInset(mStableInsets.top, mContentInsets.top);
- } else {
- return 0;
- }
- }
-
- private boolean isNavigationBarColorViewVisible() {
- return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mSysUiVis, mNavigationBarColor, mWindowFlags, false /* force */);
- }
-
- @VisibleForTesting
- void drawStatusBarBackground(Canvas c, Rect frame, int statusBarHeight) {
- if (statusBarHeight > 0 && c.getWidth() > frame.right) {
- final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right,
- mContentInsets.right);
- c.drawRect(frame.right, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
- }
- }
-
- @VisibleForTesting
- void drawNavigationBarBackground(Canvas c) {
- final Rect navigationBarRect = new Rect();
- getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
- navigationBarRect);
- final boolean visible = isNavigationBarColorViewVisible();
- if (visible && !navigationBarRect.isEmpty()) {
- c.drawRect(navigationBarRect, mNavigationBarPaint);
+ c.drawRect(0, b.getHeight(), c.getWidth(), c.getHeight(), mFillBackgroundPaint);
}
}
@@ -432,10 +211,10 @@
}
};
- @VisibleForTesting
- static class Window extends BaseIWindow {
+ private static class Window extends BaseIWindow {
private TaskSnapshotSurface mOuter;
+
public void setOuter(TaskSnapshotSurface outer) {
mOuter = outer;
}
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
index c976fe5..d834e25 100644
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ b/services/core/jni/com_android_server_location_ContextHubService.cpp
@@ -929,15 +929,15 @@
db.jniInfo.contextHubInfoCtor);
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub.hubId);
- jstrBuf = env->NewStringUTF(hub.name);
+ jstrBuf = env->NewStringUTF(hub.name.c_str());
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetName, jstrBuf);
env->DeleteLocalRef(jstrBuf);
- jstrBuf = env->NewStringUTF(hub.vendor);
+ jstrBuf = env->NewStringUTF(hub.vendor.c_str());
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetVendor, jstrBuf);
env->DeleteLocalRef(jstrBuf);
- jstrBuf = env->NewStringUTF(hub.toolchain);
+ jstrBuf = env->NewStringUTF(hub.toolchain.c_str());
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchain, jstrBuf);
env->DeleteLocalRef(jstrBuf);
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 590bce1..61a9294 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -23,7 +23,6 @@
import android.net.apf.ApfCapabilities;
import android.net.apf.ApfFilter;
import android.net.DhcpResults;
-import android.net.INetd;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -35,12 +34,10 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpManagerEvent;
import android.net.util.MultinetworkPolicyTracker;
-import android.net.util.NetdService;
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.LocalLog;
@@ -1030,16 +1027,14 @@
private boolean startIPv6() {
// Set privacy extensions.
- final String PREFER_TEMPADDRS = "2";
try {
- NetdService.run((INetd netd) -> {
- netd.setProcSysNet(
- INetd.IPV6, INetd.CONF, mInterfaceName, "use_tempaddr",
- PREFER_TEMPADDRS);
- });
+ mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
mNwService.enableIpv6(mInterfaceName);
- } catch (IllegalStateException|RemoteException|ServiceSpecificException e) {
- logError("Unable to change interface settings: %s", e);
+ } catch (RemoteException re) {
+ logError("Unable to change interface settings: %s", re);
+ return false;
+ } catch (IllegalStateException ie) {
+ logError("Unable to change interface settings: %s", ie);
return false;
}
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 40af2f8..ad593be 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -497,149 +497,6 @@
new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
}
- @Test
- public void testUpdate_userLockedImportance() throws Exception {
- // all fields locked by user
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-
- mHelper.createNotificationChannel(PKG, UID, channel, false);
-
- // same id, try to update
- final NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
-
- mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
- // no fields should be changed
- assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
- }
-
- @Test
- public void testUpdate_userLockedVisibility() throws Exception {
- // all fields locked by user
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
-
- mHelper.createNotificationChannel(PKG, UID, channel, false);
-
- // same id, try to update
- final NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
- channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
-
- mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
- // no fields should be changed
- assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
- }
-
- @Test
- public void testUpdate_userLockedVibration() throws Exception {
- // all fields locked by user
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.enableLights(false);
- channel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
-
- mHelper.createNotificationChannel(PKG, UID, channel, false);
-
- // same id, try to update
- final NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
- channel2.enableVibration(true);
- channel2.setVibrationPattern(new long[]{100});
-
- mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
- // no fields should be changed
- assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
- }
-
- @Test
- public void testUpdate_userLockedLights() throws Exception {
- // all fields locked by user
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.enableLights(false);
- channel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
-
- mHelper.createNotificationChannel(PKG, UID, channel, false);
-
- // same id, try to update
- final NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
- channel2.enableLights(true);
-
- mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
- // no fields should be changed
- assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
- }
-
- @Test
- public void testUpdate_userLockedPriority() throws Exception {
- // all fields locked by user
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setBypassDnd(true);
- channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
-
- mHelper.createNotificationChannel(PKG, UID, channel, false);
-
- // same id, try to update all fields
- final NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
- channel2.setBypassDnd(false);
-
- mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
- // no fields should be changed
- assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
- }
-
- @Test
- public void testUpdate_userLockedRingtone() throws Exception {
- // all fields locked by user
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
- channel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
-
- mHelper.createNotificationChannel(PKG, UID, channel, false);
-
- // same id, try to update all fields
- final NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
- channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes);
-
- mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
- // no fields should be changed
- assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
- }
-
- @Test
- public void testUpdate_userLockedBadge() throws Exception {
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setShowBadge(true);
- channel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
-
- mHelper.createNotificationChannel(PKG, UID, channel, false);
-
- final NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
- channel2.setShowBadge(false);
-
- mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
- // no fields should be changed
- assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
- }
@Test
public void testUpdate() throws Exception {
@@ -816,30 +673,6 @@
}
@Test
- public void testUpdateDeletedChannels() throws Exception {
- NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
-
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-
- channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
- try {
- mHelper.updateNotificationChannel(PKG, UID, channel);
- fail("Updated deleted channel");
- } catch (IllegalArgumentException e) {
- // :)
- }
-
- try {
- mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel);
- fail("Updated deleted channel");
- } catch (IllegalArgumentException e) {
- // :)
- }
- }
-
- @Test
public void testCreateDeletedChannel() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
new file mode 100644
index 0000000..19defe1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 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.am;
+
+import static com.android.server.am.ActivityManagerService.Injector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.AppOpsService;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+
+/**
+ * Test class for {@link CoreSettingsObserver}.
+ *
+ * To run the tests, use
+ *
+ * runtest -c com.android.server.am.CoreSettingsObserverTest frameworks-services
+ *
+ * or the following steps:
+ *
+ * Build: m FrameworksServicesTests
+ * Install: adb install -r \
+ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * Run: adb shell am instrument -e class com.android.server.am.CoreSettingsObserverTest -w \
+ * com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CoreSettingsObserverTest {
+ private static final String TEST_SETTING_SECURE_INT = "secureInt";
+ private static final String TEST_SETTING_GLOBAL_FLOAT = "globalFloat";
+ private static final String TEST_SETTING_SYSTEM_STRING = "systemString";
+
+ private static final int TEST_INT = 111;
+ private static final float TEST_FLOAT = 3.14f;
+ private static final String TEST_STRING = "testString";
+
+ private ActivityManagerService mAms;
+ @Mock private Context mContext;
+
+ private MockContentResolver mContentResolver;
+ private CoreSettingsObserver mCoreSettingsObserver;
+
+ @BeforeClass
+ public static void setupOnce() {
+ CoreSettingsObserver.sSecureSettingToTypeMap.put(TEST_SETTING_SECURE_INT, int.class);
+ CoreSettingsObserver.sGlobalSettingToTypeMap.put(TEST_SETTING_GLOBAL_FLOAT, float.class);
+ CoreSettingsObserver.sSystemSettingToTypeMap.put(TEST_SETTING_SYSTEM_STRING, String.class);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final Context originalContext = InstrumentationRegistry.getContext();
+ when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
+ mContentResolver = new MockContentResolver(mContext);
+ mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+
+ mAms = new ActivityManagerService(new TestInjector());
+ mCoreSettingsObserver = new CoreSettingsObserver(mAms);
+ }
+
+ @Test
+ public void testPopulateSettings() {
+ Settings.Secure.putInt(mContentResolver, TEST_SETTING_SECURE_INT, TEST_INT);
+ Settings.Global.putFloat(mContentResolver, TEST_SETTING_GLOBAL_FLOAT, TEST_FLOAT);
+ Settings.System.putString(mContentResolver, TEST_SETTING_SYSTEM_STRING, TEST_STRING);
+
+ final Bundle settingsBundle = getPopulatedBundle();
+
+ assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
+ TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
+ assertEquals("Unexpected value of " + TEST_SETTING_GLOBAL_FLOAT,
+ TEST_FLOAT, settingsBundle.getFloat(TEST_SETTING_GLOBAL_FLOAT), 0);
+ assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
+ TEST_STRING, settingsBundle.getString(TEST_SETTING_SYSTEM_STRING));
+ }
+
+ @Test
+ public void testPopulateSettings_settingNotSet() {
+ final Bundle settingsBundle = getPopulatedBundle();
+
+ assertFalse("Bundle should not contain " + TEST_SETTING_SECURE_INT,
+ settingsBundle.containsKey(TEST_SETTING_SECURE_INT));
+ assertFalse("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT,
+ settingsBundle.containsKey(TEST_SETTING_GLOBAL_FLOAT));
+ assertFalse("Bundle should not contain " + TEST_SETTING_SYSTEM_STRING,
+ settingsBundle.containsKey(TEST_SETTING_SYSTEM_STRING));
+ }
+
+ private Bundle getPopulatedBundle() {
+ final Bundle settingsBundle = new Bundle();
+ mCoreSettingsObserver.populateSettings(settingsBundle,
+ CoreSettingsObserver.sGlobalSettingToTypeMap);
+ mCoreSettingsObserver.populateSettings(settingsBundle,
+ CoreSettingsObserver.sSecureSettingToTypeMap);
+ mCoreSettingsObserver.populateSettings(settingsBundle,
+ CoreSettingsObserver.sSystemSettingToTypeMap);
+ return settingsBundle;
+ }
+
+ private class TestInjector extends Injector {
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return null;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return null;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 0f1b81e..7a4746a 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.app.ActivityManager;
import android.app.IUserSwitchObserver;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -49,16 +50,20 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.server.am.ActivityManagerService.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.ActivityManagerService.REPORT_LOCKED_BOOT_COMPLETE_MSG;
import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG;
import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG;
import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG;
import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -71,9 +76,29 @@
private UserController mUserController;
private TestInjector mInjector;
+ private static final List<String> START_FOREGROUND_USER_ACTIONS =
+ Arrays.asList(
+ Intent.ACTION_USER_STARTED,
+ Intent.ACTION_USER_SWITCHED,
+ Intent.ACTION_USER_STARTING);
+
+ private static final List<String> START_BACKGROUND_USER_ACTIONS =
+ Arrays.asList(
+ Intent.ACTION_USER_STARTED,
+ Intent.ACTION_LOCKED_BOOT_COMPLETED,
+ Intent.ACTION_USER_STARTING);
+
+ private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES =
+ new HashSet<>(Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG,
+ SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG));
+
+ private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES =
+ new HashSet<>(Arrays.asList(SYSTEM_USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG));
+
@Override
public void setUp() throws Exception {
super.setUp();
+ System.setProperty("dexmaker.share_classloader", "true");
mInjector = new TestInjector(getContext());
mUserController = new UserController(mInjector);
setUpUser(TEST_USER_ID, 0);
@@ -83,39 +108,62 @@
protected void tearDown() throws Exception {
super.tearDown();
mInjector.handlerThread.quit();
-
}
@SmallTest
- public void testStartUser() throws RemoteException {
- mUserController.startUser(TEST_USER_ID, true);
+ public void testStartUser_foreground() throws RemoteException {
+ mUserController.startUser(TEST_USER_ID, true /* foreground */);
Mockito.verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true);
- startUserAssertions();
+ Mockito.verify(mInjector.getActivityStackSupervisor()).setLockTaskModeLocked(
+ nullable(TaskRecord.class),
+ eq(ActivityManager.LOCK_TASK_MODE_NONE),
+ anyString(),
+ anyBoolean());
+ startForegroundUserAssertions();
+ }
+
+ @SmallTest
+ public void testStartUser_background() throws RemoteException {
+ mUserController.startUser(TEST_USER_ID, false /* foreground */);
+ Mockito.verify(
+ mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+ Mockito.verify(mInjector.getActivityStackSupervisor(), never()).setLockTaskModeLocked(
+ nullable(TaskRecord.class),
+ eq(ActivityManager.LOCK_TASK_MODE_NONE),
+ anyString(),
+ anyBoolean());
+ startBackgroundUserAssertions();
}
@SmallTest
public void testStartUserUIDisabled() throws RemoteException {
mUserController.mUserSwitchUiEnabled = false;
- mUserController.startUser(TEST_USER_ID, true);
+ mUserController.startUser(TEST_USER_ID, true /* foreground */);
Mockito.verify(mInjector.getWindowManager(), never())
.startFreezingScreen(anyInt(), anyInt());
Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
- startUserAssertions();
+ startForegroundUserAssertions();
}
- private void startUserAssertions() throws RemoteException {
- List<String> expectedActions = Arrays.asList(Intent.ACTION_USER_STARTED,
- Intent.ACTION_USER_SWITCHED, Intent.ACTION_USER_STARTING);
+ private void startUserAssertions(
+ List<String> expectedActions, Set<Integer> expectedMessageCodes)
+ throws RemoteException {
assertEquals(expectedActions, getActions(mInjector.sentIntents));
- Set<Integer> expectedCodes = new HashSet<>(
- Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG,
- SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG));
Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
- assertEquals("Unexpected message sent", expectedCodes, actualCodes);
+ assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes);
+ }
+
+ private void startBackgroundUserAssertions() throws RemoteException {
+ startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES);
+ }
+
+ private void startForegroundUserAssertions() throws RemoteException {
+ startUserAssertions(START_FOREGROUND_USER_ACTIONS, START_FOREGROUND_USER_MESSAGE_CODES);
Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
assertNotNull(reportMsg);
UserState userState = (UserState) reportMsg.obj;
@@ -275,6 +323,7 @@
UserManagerService userManagerMock;
UserManagerInternal userManagerInternalMock;
WindowManagerService windowManagerMock;
+ ActivityStackSupervisor activityStackSupervisor;
private Context mCtx;
List<Intent> sentIntents = new ArrayList<>();
@@ -287,6 +336,7 @@
userManagerMock = mock(UserManagerService.class);
userManagerInternalMock = mock(UserManagerInternal.class);
windowManagerMock = mock(WindowManagerService.class);
+ activityStackSupervisor = mock(ActivityStackSupervisor.class);
}
@Override
@@ -321,12 +371,6 @@
}
@Override
- void stackSupervisorSetLockTaskModeLocked(TaskRecord task, int lockTaskModeState,
- String reason, boolean andResume) {
- Log.i(TAG, "stackSupervisorSetLockTaskModeLocked");
- }
-
- @Override
WindowManagerService getWindowManager() {
return windowManagerMock;
}
@@ -347,16 +391,15 @@
}
@Override
- boolean stackSupervisorSwitchUserLocked(int userId, UserState uss) {
- Log.i(TAG, "stackSupervisorSwitchUserLocked " + userId);
- return true;
- }
-
- @Override
void startHomeActivityLocked(int userId, String reason) {
Log.i(TAG, "startHomeActivityLocked " + userId);
}
- }
+
+ @Override
+ ActivityStackSupervisor getActivityStackSupervisor() {
+ return activityStackSupervisor;
+ }
+ }
private static class TestHandler extends Handler {
private final List<Message> mMessages = new ArrayList<>();
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 72fb78e..afc0f67 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -360,6 +360,19 @@
assertNull(mDexManager.getPackageUseInfo(frameworkDex));
}
+ @Test
+ public void testNotifySecondaryFromProtected() {
+ // Foo loads its own secondary files.
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
+ notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+ assertNotNull(pui);
+ assertFalse(pui.isUsedByOtherApps());
+ assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+ assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
for (String dex : secondaries) {
@@ -394,6 +407,8 @@
ai.setBaseCodePath(codeDir + "/base.dex");
ai.setSplitCodePaths(new String[] {codeDir + "/split-1.dex", codeDir + "/split-2.dex"});
ai.dataDir = "/data/user/" + userId + "/" + packageName;
+ ai.deviceProtectedDataDir = "/data/user_de/" + userId + "/" + packageName;
+ ai.credentialProtectedDataDir = "/data/user_ce/" + userId + "/" + packageName;
ai.packageName = packageName;
return ai;
}
@@ -426,6 +441,13 @@
return paths;
}
+ List<String> getSecondaryDexPathsFromProtectedDirs() {
+ List<String> paths = new ArrayList<>();
+ paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex");
+ paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex");
+ return paths;
+ }
+
List<String> getBaseAndSplitDexPaths() {
List<String> paths = new ArrayList<>();
paths.add(mPackageInfo.applicationInfo.sourceDir);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 717ddf2..aab75ee 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -16,9 +16,6 @@
package com.android.server.wm;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -27,19 +24,15 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.GraphicBuffer;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.view.Surface;
-import com.android.server.wm.TaskSnapshotSurface.Window;
-
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,174 +48,59 @@
private TaskSnapshotSurface mSurface;
- private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis,
- int windowFlags, Rect taskBounds) {
- final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
- GraphicBuffer.USAGE_SW_READ_NEVER | GraphicBuffer.USAGE_SW_WRITE_NEVER);
- final TaskSnapshot snapshot = new TaskSnapshot(buffer,
- ORIENTATION_PORTRAIT, contentInsets, false, 1.0f);
- mSurface = new TaskSnapshotSurface(sWm, new Window(), new Surface(), snapshot, "Test",
- Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds);
- }
-
- private void setupSurface(int width, int height) {
- setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- new Rect(0, 0, width, height));
+ @Before
+ public void setUp() {
+ mSurface = new TaskSnapshotSurface(null, null, null, Color.WHITE);
}
@Test
public void fillEmptyBackground_fillHorizontally() throws Exception {
- setupSurface(200, 100);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(200);
when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200));
+ final Bitmap b = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
+ mSurface.fillEmptyBackground(mockCanvas, b);
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
}
@Test
public void fillEmptyBackground_fillVertically() throws Exception {
- setupSurface(100, 200);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(200);
- mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100));
+ final Bitmap b = Bitmap.createBitmap(200, 100, Config.ARGB_8888);
+ mSurface.fillEmptyBackground(mockCanvas, b);
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any());
}
@Test
public void fillEmptyBackground_fillBoth() throws Exception {
- setupSurface(200, 200);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(200);
when(mockCanvas.getHeight()).thenReturn(200);
- mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
+ final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ mSurface.fillEmptyBackground(mockCanvas, b);
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any());
}
@Test
public void fillEmptyBackground_dontFill_sameSize() throws Exception {
- setupSurface(100, 100);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
+ final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ mSurface.fillEmptyBackground(mockCanvas, b);
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
}
@Test
public void fillEmptyBackground_dontFill_bitmapLarger() throws Exception {
- setupSurface(100, 100);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200));
+ final Bitmap b = Bitmap.createBitmap(200, 200, Config.ARGB_8888);
+ mSurface.fillEmptyBackground(mockCanvas, b);
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
}
-
- @Test
- public void testCalculateSnapshotCrop() {
- setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(0, 0, 100, 90), mSurface.calculateSnapshotCrop());
- }
-
- @Test
- public void testCalculateSnapshotCrop_taskNotOnTop() {
- setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100));
- assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop());
- }
-
- @Test
- public void testCalculateSnapshotCrop_navBarLeft() {
- setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(10, 0, 100, 100), mSurface.calculateSnapshotCrop());
- }
-
- @Test
- public void testCalculateSnapshotCrop_navBarRight() {
- setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(0, 0, 90, 100), mSurface.calculateSnapshotCrop());
- }
-
- @Test
- public void testCalculateSnapshotFrame() {
- setupSurface(100, 100);
- final Rect insets = new Rect(0, 10, 0, 10);
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
- assertEquals(new Rect(0, -10, 100, 70),
- mSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
- }
-
- @Test
- public void testCalculateSnapshotFrame_navBarLeft() {
- setupSurface(100, 100);
- final Rect insets = new Rect(10, 10, 0, 0);
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
- assertEquals(new Rect(0, -10, 90, 80),
- mSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
- }
-
- @Test
- public void testDrawStatusBarBackground() {
- setupSurface(100, 100);
- final Rect insets = new Rect(0, 10, 10, 0);
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100), 10);
- verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
- }
-
- @Test
- public void testDrawStatusBarBackground_nope() {
- setupSurface(100, 100);
- final Rect insets = new Rect(0, 10, 10, 0);
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100), 10);
- verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
- }
-
- @Test
- public void testDrawNavigationBarBackground() {
- final Rect insets = new Rect(0, 10, 0, 10);
- setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- new Rect(0, 0, 100, 100));
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawNavigationBarBackground(mockCanvas);
- verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any());
- }
-
- @Test
- public void testDrawNavigationBarBackground_left() {
- final Rect insets = new Rect(10, 10, 0, 0);
- setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- new Rect(0, 0, 100, 100));
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawNavigationBarBackground(mockCanvas);
- verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any());
- }
-
- @Test
- public void testDrawNavigationBarBackground_right() {
- final Rect insets = new Rect(0, 10, 10, 0);
- setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- new Rect(0, 0, 100, 100));
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawNavigationBarBackground(mockCanvas);
- verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any());
- }
}